PHOENIX-1504 Support adding column to a table that has views (Samarth Jain/Dave 
Hacker)


Project: http://git-wip-us.apache.org/repos/asf/phoenix/repo
Commit: http://git-wip-us.apache.org/repos/asf/phoenix/commit/e78eb6fa
Tree: http://git-wip-us.apache.org/repos/asf/phoenix/tree/e78eb6fa
Diff: http://git-wip-us.apache.org/repos/asf/phoenix/diff/e78eb6fa

Branch: refs/heads/json
Commit: e78eb6faceec40d8b09fbc7dde778b87fe54feef
Parents: 2d70eff
Author: Samarth <samarth.j...@salesforce.com>
Authored: Thu Jun 18 15:37:37 2015 -0700
Committer: Samarth <samarth.j...@salesforce.com>
Committed: Thu Jun 18 15:37:37 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 356 +++++++++++++++++
 .../end2end/TenantSpecificTablesDDLIT.java      |  20 +-
 .../org/apache/phoenix/end2end/UpgradeIT.java   | 332 ++++++++++++++++
 .../coprocessor/MetaDataEndpointImpl.java       | 262 +++++++++---
 .../phoenix/coprocessor/MetaDataProtocol.java   |   4 +-
 .../coprocessor/generated/PTableProtos.java     | 103 ++++-
 .../phoenix/jdbc/PhoenixDatabaseMetaData.java   |   3 +-
 .../query/ConnectionQueryServicesImpl.java      |  51 ++-
 .../apache/phoenix/query/QueryConstants.java    |  30 +-
 .../apache/phoenix/schema/DelegateTable.java    |   5 +
 .../apache/phoenix/schema/MetaDataClient.java   |  37 +-
 .../java/org/apache/phoenix/schema/PTable.java  |   1 +
 .../org/apache/phoenix/schema/PTableImpl.java   |  40 +-
 .../java/org/apache/phoenix/util/ByteUtil.java  |  10 +-
 .../org/apache/phoenix/util/UpgradeUtil.java    | 395 ++++++++++++++++++-
 15 files changed, 1495 insertions(+), 154 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
index 59698d6..61dd6a9 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/AlterTableIT.java
@@ -18,6 +18,7 @@
 package org.apache.phoenix.end2end;
 
 import static 
org.apache.hadoop.hbase.HColumnDescriptor.DEFAULT_REPLICATION_SCOPE;
+import static 
org.apache.phoenix.exception.SQLExceptionCode.CANNOT_MUTATE_TABLE;
 import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES;
 import static org.apache.phoenix.util.TestUtil.closeConnection;
 import static org.apache.phoenix.util.TestUtil.closeStatement;
@@ -32,9 +33,11 @@ import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 
@@ -48,8 +51,10 @@ import org.apache.phoenix.exception.SQLExceptionCode;
 import org.apache.phoenix.jdbc.PhoenixConnection;
 import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
 import org.apache.phoenix.query.QueryConstants;
+import org.apache.phoenix.schema.PColumn;
 import org.apache.phoenix.schema.PTable;
 import org.apache.phoenix.schema.PTableKey;
+import org.apache.phoenix.schema.PTableType;
 import org.apache.phoenix.schema.TableNotFoundException;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.PhoenixRuntime;
@@ -59,6 +64,8 @@ import org.apache.phoenix.util.SchemaUtil;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
+import com.google.common.base.Objects;
+
 /**
  *
  * A lot of tests in this class test HBase level properties. As a result,
@@ -1988,4 +1995,353 @@ public class AlterTableIT extends 
BaseOwnClusterHBaseManagedTimeIT {
             conn.close();
         }
     }
+    
+    @Test
+    public void testAddColumnToTableWithViews() throws Exception {
+        Connection conn = DriverManager.getConnection(getUrl());
+        try {       
+            conn.createStatement().execute("CREATE TABLE IF NOT EXISTS 
TABLEWITHVIEW ("
+                    + " ID char(1) NOT NULL,"
+                    + " COL1 integer NOT NULL,"
+                    + " COL2 bigint NOT NULL,"
+                    + " CONSTRAINT NAME_PK PRIMARY KEY (ID, COL1, COL2)"
+                    + " )");
+            assertTableDefinition(conn, "TABLEWITHVIEW", PTableType.TABLE, 
null, 0, 3, -1, "ID", "COL1", "COL2");
+            
+            conn.createStatement().execute("CREATE VIEW VIEWOFTABLE ( 
VIEW_COL1 SMALLINT ) AS SELECT * FROM TABLEWITHVIEW");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, 
"TABLEWITHVIEW", 0, 4, 3, "ID", "COL1", "COL2", "VIEW_COL1");
+            
+            conn.createStatement().execute("ALTER TABLE TABLEWITHVIEW ADD COL3 
char(10)");
+            assertTableDefinition(conn, "VIEWOFTABLE", PTableType.VIEW, 
"TABLEWITHVIEW", 1, 5, 4, "ID", "COL1", "COL2", "COL3", "VIEW_COL1");
+            
+        } finally {
+            conn.close();
+        }
+    }
+
+    private void assertTableDefinition(Connection conn, String tableName, 
PTableType tableType, String parentTableName, int sequenceNumber, int 
columnCount, int baseColumnCount, String... columnName) throws Exception {
+        PreparedStatement p = conn.prepareStatement("SELECT * FROM 
SYSTEM.CATALOG WHERE TABLE_NAME=? AND TABLE_TYPE=?");
+        p.setString(1, tableName);
+        p.setString(2, tableType.getSerializedValue());
+        ResultSet rs = p.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(getSystemCatalogEntriesForTable(conn, tableName, 
"Mismatch in BaseColumnCount"), baseColumnCount, 
rs.getInt("BASE_COLUMN_COUNT"));
+        assertEquals(getSystemCatalogEntriesForTable(conn, tableName, 
"Mismatch in columnCount"), columnCount, rs.getInt("COLUMN_COUNT"));
+        assertEquals(getSystemCatalogEntriesForTable(conn, tableName, 
"Mismatch in sequenceNumber"), sequenceNumber, rs.getInt("TABLE_SEQ_NUM"));
+        rs.close();
+
+        ResultSet parentTableColumnsRs = null; 
+        if (parentTableName != null) {
+            parentTableColumnsRs = conn.getMetaData().getColumns(null, null, 
parentTableName, null);
+        }
+        
+        rs = conn.getMetaData().getColumns(null, null, tableName, null);
+        for (int i = 0; i < columnName.length; i++) {
+            if (columnName[i] != null) {
+                assertTrue(rs.next());
+                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, 
"Mismatch in columnName: i=" + i), columnName[i], rs.getString("COLUMN_NAME"));
+                assertEquals(getSystemCatalogEntriesForTable(conn, tableName, 
"Mismatch in ordinalPosition: i=" + i), i+1, rs.getInt("ORDINAL_POSITION"));
+                if (i < baseColumnCount && parentTableColumnsRs != null) {
+                    assertTrue(parentTableColumnsRs.next());
+                    ResultSetMetaData md = parentTableColumnsRs.getMetaData();
+                    assertEquals(md.getColumnCount(), 
rs.getMetaData().getColumnCount());
+                    for (int columnIndex = 1; columnIndex < 
md.getColumnCount(); columnIndex++) {
+                        String viewColumnValue = rs.getString(columnIndex);
+                        String parentTableColumnValue = 
parentTableColumnsRs.getString(columnIndex);
+                        if (!Objects.equal(viewColumnValue, 
parentTableColumnValue)) {
+                            if 
(md.getColumnName(columnIndex).equals("TABLE_NAME")) {
+                                assertEquals(parentTableName, 
parentTableColumnValue);
+                                assertEquals(tableName, viewColumnValue);
+                            } else {
+                                fail(md.getColumnName(columnIndex) + "=" + 
parentTableColumnValue);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        assertFalse(getSystemCatalogEntriesForTable(conn, tableName, ""), 
rs.next());
+    }
+    
+    private String getSystemCatalogEntriesForTable(Connection conn, String 
tableName, String message) throws Exception {
+        StringBuilder sb = new StringBuilder(message);
+        sb.append("\n\n\n");
+        ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM 
SYSTEM.CATALOG WHERE TABLE_NAME='"+ tableName +"'");
+        ResultSetMetaData metaData = rs.getMetaData();
+        int rowNum = 0;
+        while (rs.next()) {
+            sb.append(rowNum++).append("------\n");
+            for (int i = 1; i <= metaData.getColumnCount(); i++) {
+                
sb.append("\t").append(metaData.getColumnLabel(i)).append("=").append(rs.getString(i)).append("\n");
+            }
+            sb.append("\n");
+        }
+        rs.close();
+        return sb.toString();
+    }
+    
+    @Test
+    public void testCacheInvalidatedAfterAddingColumnToBaseTableWithViews() 
throws Exception {
+        String baseTable = 
"testCacheInvalidatedAfterAddingColumnToBaseTableWithViews";
+        String viewName = baseTable + "_view";
+        String tenantId = "tenantId";
+        try (Connection globalConn = DriverManager.getConnection(getUrl())) {
+            String tableDDL = "CREATE TABLE " + baseTable + " (TENANT_ID 
VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 VARCHAR CONSTRAINT NAME_PK PRIMARY 
KEY(TENANT_ID, PK1)) MULTI_TENANT = true " ;
+            globalConn.createStatement().execute(tableDDL);
+            Properties tenantProps = new Properties();
+            tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
+            // create a tenant specific view
+            try (Connection tenantConn =  
DriverManager.getConnection(getUrl(), tenantProps)) {
+                String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * 
FROM " + baseTable;
+                tenantConn.createStatement().execute(viewDDL);
+                
+                // Add a column to the base table using global connection
+                globalConn.createStatement().execute("ALTER TABLE " + 
baseTable + " ADD NEW_COL VARCHAR");
+
+                // Check now whether the tenant connection can see the column 
that was added
+                tenantConn.createStatement().execute("SELECT NEW_COL FROM " + 
viewName);
+                tenantConn.createStatement().execute("SELECT NEW_COL FROM " + 
baseTable);
+            }
+        }
+    }
+    
+    @Test
+    public void testDropColumnOnTableWithViewsNotAllowed() throws Exception {
+        String baseTable = "testDropColumnOnTableWithViewsNotAllowed";
+        String viewName = baseTable + "_view";
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            String tableDDL = "CREATE TABLE " + baseTable + " (PK1 VARCHAR NOT 
NULL PRIMARY KEY, V1 VARCHAR, V2 VARCHAR)";
+            conn.createStatement().execute(tableDDL);
+            
+            String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " 
+ baseTable;
+            conn.createStatement().execute(viewDDL);
+            
+            String dropColumn = "ALTER TABLE " + baseTable + " DROP COLUMN V2";
+            try {
+                conn.createStatement().execute(dropColumn);
+                fail("Dropping column on a base table that has views is not 
allowed");
+            } catch (SQLException e) {  
+                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), 
e.getErrorCode());
+            }
+        }
+    }
+    
+    @Test
+    public void testAlteringViewThatHasChildViewsNotAllowed() throws Exception 
{
+        String baseTable = "testAlteringViewThatHasChildViewsNotAllowed";
+        String childView = "childView";
+        String grandChildView = "grandChildView";
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            String baseTableDDL =
+                    "CREATE TABLE " +  baseTable + " (TENANT_ID VARCHAR NOT 
NULL, PK2 VARCHAR NOT NULL, V1 VARCHAR, V2 VARCHAR CONSTRAINT NAME_PK PRIMARY 
KEY(TENANT_ID, PK2))";
+            conn.createStatement().execute(baseTableDDL);
+
+            String childViewDDL = "CREATE VIEW " + childView + " AS SELECT * 
FROM " + baseTable;
+            conn.createStatement().execute(childViewDDL);
+
+            String addColumnToChildViewDDL =
+                    "ALTER VIEW " + childView + " ADD CHILD_VIEW_COL VARCHAR";
+            conn.createStatement().execute(addColumnToChildViewDDL);
+
+            String grandChildViewDDL =
+                    "CREATE VIEW " + grandChildView + " AS SELECT * FROM " + 
childView;
+            conn.createStatement().execute(grandChildViewDDL);
+
+            // dropping base table column from child view should fail
+            String dropColumnFromChildView = "ALTER VIEW " + childView + " 
DROP COLUMN V2";
+            try {
+                conn.createStatement().execute(dropColumnFromChildView);
+                fail("Dropping columns from a view that has child views on it 
is not allowed");
+            } catch (SQLException e) {
+                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), 
e.getErrorCode());
+            }
+
+            // dropping view specific column from child view should fail
+            dropColumnFromChildView = "ALTER VIEW " + childView + " DROP 
COLUMN CHILD_VIEW_COL";
+            try {
+                conn.createStatement().execute(dropColumnFromChildView);
+                fail("Dropping columns from a view that has child views on it 
is not allowed");
+            } catch (SQLException e) {
+                
assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), 
e.getErrorCode());
+            }
+            
+            // Adding column to view that has child views should fail
+            String addColumnToChildView = "ALTER VIEW " + childView + " ADD V5 
VARCHAR";
+            try {
+                conn.createStatement().execute(addColumnToChildView);
+                fail("Adding columns to a view that has child views on it is 
not allowed");
+            } catch (SQLException e) {
+                
assertEquals(SQLExceptionCode.CANNOT_MUTATE_TABLE.getErrorCode(), 
e.getErrorCode());
+            }
+
+            // dropping column from the grand child view, however, should work.
+            String dropColumnFromGrandChildView =
+                    "ALTER VIEW " + grandChildView + " DROP COLUMN 
CHILD_VIEW_COL";
+            conn.createStatement().execute(dropColumnFromGrandChildView);
+
+            // similarly, dropping column inherited from the base table should 
work.
+            dropColumnFromGrandChildView = "ALTER VIEW " + grandChildView + " 
DROP COLUMN V2";
+            conn.createStatement().execute(dropColumnFromGrandChildView);
+        }
+    }
+    
+    @Test
+    public void testDivorcedViewsStayDivorced() throws Exception {
+        String baseTable = "testDivorcedViewsStayDivorced";
+        String viewName = baseTable + "_view";
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            String tableDDL = "CREATE TABLE " + baseTable + " (PK1 VARCHAR NOT 
NULL PRIMARY KEY, V1 VARCHAR, V2 VARCHAR)";
+            conn.createStatement().execute(tableDDL);
+            
+            String viewDDL = "CREATE VIEW " + viewName + " AS SELECT * FROM " 
+ baseTable;
+            conn.createStatement().execute(viewDDL);
+            
+            // Drop the column inherited from base table to divorce the view
+            String dropColumn = "ALTER VIEW " + viewName + " DROP COLUMN V2";
+            conn.createStatement().execute(dropColumn);
+            
+            String alterBaseTable = "ALTER TABLE " + baseTable + " ADD V3 
VARCHAR";
+            conn.createStatement().execute(alterBaseTable);
+            
+            // Column V3 shouldn't have propagated to the divorced view.
+            String sql = "SELECT V3 FROM " + viewName;
+            try {
+                conn.createStatement().execute(sql);
+            } catch (SQLException e) {
+                assertEquals(SQLExceptionCode.COLUMN_NOT_FOUND.getErrorCode(), 
e.getErrorCode());
+            }
+        } 
+    }
+    
+    @Test
+    public void testAddingColumnToBaseTablePropagatesToEntireViewHierarchy() 
throws Exception {
+        String baseTable = "testViewHierarchy";
+        String view1 = "view1";
+        String view2 = "view2";
+        String view3 = "view3";
+        String view4 = "view4";
+        /*                                     baseTable
+                                 /                  |               \ 
+                         view1(tenant1)    view3(tenant2)          
view4(global)
+                          /
+                        view2(tenant1)  
+        */
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            String baseTableDDL = "CREATE TABLE " + baseTable + " (TENANT_ID 
VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 VARCHAR, V2 VARCHAR CONSTRAINT 
NAME_PK PRIMARY KEY(TENANT_ID, PK1)) MULTI_TENANT = true ";
+            conn.createStatement().execute(baseTableDDL);
+            
+            try (Connection tenant1Conn = getTenantConnection("tenant1")) {
+                String view1DDL = "CREATE VIEW " + view1 + " AS SELECT * FROM 
" + baseTable;
+                tenant1Conn.createStatement().execute(view1DDL);
+                
+                String view2DDL = "CREATE VIEW " + view2 + " AS SELECT * FROM 
" + view1;
+                tenant1Conn.createStatement().execute(view2DDL);
+            }
+            
+            try (Connection tenant2Conn = getTenantConnection("tenant2")) {
+                String view3DDL = "CREATE VIEW " + view3 + " AS SELECT * FROM 
" + baseTable;
+                tenant2Conn.createStatement().execute(view3DDL);
+            }
+            
+            String view4DDL = "CREATE VIEW " + view4 + " AS SELECT * FROM " + 
baseTable;
+            conn.createStatement().execute(view4DDL);
+            
+            String alterBaseTable = "ALTER TABLE " + baseTable + " ADD V3 
VARCHAR";
+            conn.createStatement().execute(alterBaseTable);
+            
+            // verify that the column is visible to view4
+            conn.createStatement().execute("SELECT V3 FROM " + view4);
+            
+            // verify that the column is visible to view1 and view2
+            try (Connection tenant1Conn = getTenantConnection("tenant1")) {
+                tenant1Conn.createStatement().execute("SELECT V3 from " + 
view1);
+                tenant1Conn.createStatement().execute("SELECT V3 from " + 
view2);
+            }
+            
+            // verify that the column is visible to view3
+            try (Connection tenant2Conn = getTenantConnection("tenant2")) {
+                tenant2Conn.createStatement().execute("SELECT V3 from " + 
view3);
+            }
+            
+        }
+           
+    }
+    
+    @Test
+    public void testChangingPKOfBaseTableChangesPKForAllViews() throws 
Exception {
+        String baseTable = "testChangePKOfBaseTable";
+        String view1 = "view1";
+        String view2 = "view2";
+        String view3 = "view3";
+        String view4 = "view4";
+        /*                                     baseTable
+                                 /                  |               \ 
+                         view1(tenant1)    view3(tenant2)          
view4(global)
+                          /
+                        view2(tenant1)  
+         */
+        Connection tenant1Conn = null, tenant2Conn = null;
+        try (Connection globalConn = DriverManager.getConnection(getUrl())) {
+            String baseTableDDL = "CREATE TABLE "
+                    + baseTable
+                    + " (TENANT_ID VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 
VARCHAR, V2 VARCHAR CONSTRAINT NAME_PK PRIMARY KEY(TENANT_ID, PK1)) 
MULTI_TENANT = true ";
+            globalConn.createStatement().execute(baseTableDDL);
+
+            tenant1Conn = getTenantConnection("tenant1");
+            String view1DDL = "CREATE VIEW " + view1 + " AS SELECT * FROM " + 
baseTable;
+            tenant1Conn.createStatement().execute(view1DDL);
+
+            String view2DDL = "CREATE VIEW " + view2 + " AS SELECT * FROM " + 
view1;
+            tenant1Conn.createStatement().execute(view2DDL);
+
+            tenant2Conn = getTenantConnection("tenant2");
+            String view3DDL = "CREATE VIEW " + view3 + " AS SELECT * FROM " + 
baseTable;
+            tenant2Conn.createStatement().execute(view3DDL);
+
+            String view4DDL = "CREATE VIEW " + view4 + " AS SELECT * FROM " + 
baseTable;
+            globalConn.createStatement().execute(view4DDL);
+
+            String alterBaseTable = "ALTER TABLE " + baseTable + " ADD NEW_PK 
varchar primary key ";
+            globalConn.createStatement().execute(alterBaseTable);
+
+            // verify that the new column new_pk is now part of the primary 
key for the entire hierarchy
+            
assertTrue(checkColumnPartOfPk(globalConn.unwrap(PhoenixConnection.class), 
"PK1", baseTable));
+            
assertTrue(checkColumnPartOfPk(tenant1Conn.unwrap(PhoenixConnection.class), 
"PK1", view1));
+            
assertTrue(checkColumnPartOfPk(tenant1Conn.unwrap(PhoenixConnection.class), 
"PK1", view2));
+            
assertTrue(checkColumnPartOfPk(tenant2Conn.unwrap(PhoenixConnection.class), 
"PK1", view3));
+            
assertTrue(checkColumnPartOfPk(globalConn.unwrap(PhoenixConnection.class), 
"PK1", view4));
+
+        } finally {
+            if (tenant1Conn != null) {
+                try {
+                    tenant1Conn.close();
+                } catch (Throwable ignore) {}
+            }
+            if (tenant2Conn != null) {
+                try {
+                    tenant2Conn.close();
+                } catch (Throwable ignore) {}
+            }
+        }
+
+    }
+    
+    private boolean checkColumnPartOfPk(PhoenixConnection conn, String 
columnName, String tableName) throws SQLException {
+        String normalizedTableName = SchemaUtil.normalizeIdentifier(tableName);
+        PTable table = conn.getMetaDataCache().getTable(new 
PTableKey(conn.getTenantId(), normalizedTableName));
+        List<PColumn> pkCols = table.getPKColumns();
+        String normalizedColumnName = 
SchemaUtil.normalizeIdentifier(columnName);
+        for (PColumn pkCol : pkCols) {
+            if (pkCol.getName().getString().equals(normalizedColumnName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+    
+    private Connection getTenantConnection(String tenantId) throws Exception {
+        Properties tenantProps = new Properties();
+        tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
+        return DriverManager.getConnection(getUrl(), tenantProps);
+    }
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
index a7c7291..e1a1970 100644
--- 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/TenantSpecificTablesDDLIT.java
@@ -272,6 +272,7 @@ public class TenantSpecificTablesDDLIT extends 
BaseTenantSpecificTablesIT {
                 assertEquals(CANNOT_MODIFY_VIEW_PK.getErrorCode(), 
expected.getErrorCode());
             }
             
+            // try removing a non-PK col
             try {
                 conn.createStatement().execute("alter table " + 
TENANT_TABLE_NAME + " drop column id");
                 fail();
@@ -291,25 +292,6 @@ public class TenantSpecificTablesDDLIT extends 
BaseTenantSpecificTablesIT {
         props.setProperty(PhoenixRuntime.CURRENT_SCN_ATTRIB, 
Long.toString(nextTimestamp()));
         Connection conn = DriverManager.getConnection(getUrl(), props);
         try {
-            // try adding a PK col
-            try {
-                conn.createStatement().execute("alter table " + 
PARENT_TABLE_NAME + " add new_pk varchar primary key");
-                fail();
-            }
-            catch (SQLException expected) {
-                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), 
expected.getErrorCode());
-            }
-            
-            // try adding a non-PK col
-            try {
-                conn.createStatement().execute("alter table " + 
PARENT_TABLE_NAME + " add new_col char(1)");
-                fail();
-            }
-            catch (SQLException expected) {
-                assertEquals(CANNOT_MUTATE_TABLE.getErrorCode(), 
expected.getErrorCode());
-            }
-            
-            // try removing a PK col
             try {
                 conn.createStatement().execute("alter table " + 
PARENT_TABLE_NAME + " drop column id");
                 fail();

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
new file mode 100644
index 0000000..886e567
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/UpgradeIT.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.phoenix.end2end;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+import static 
org.apache.phoenix.query.QueryConstants.BASE_TABLE_BASE_COLUMN_COUNT;
+import static 
org.apache.phoenix.query.QueryConstants.DIVORCED_VIEW_BASE_COLUMN_COUNT;
+import static 
org.apache.phoenix.util.UpgradeUtil.SELECT_BASE_COLUMN_COUNT_FROM_HEADER_ROW;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.client.HTableInterface;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.RowMutations;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.phoenix.jdbc.PhoenixConnection;
+import org.apache.phoenix.jdbc.PhoenixDatabaseMetaData;
+import org.apache.phoenix.util.PhoenixRuntime;
+import org.apache.phoenix.util.SchemaUtil;
+import org.apache.phoenix.util.UpgradeUtil;
+import org.junit.Test;
+
+public class UpgradeIT extends BaseHBaseManagedTimeIT {
+
+    private static String TENANT_ID = "tenantId";
+
+    @Test
+    public void testUpgradeForTenantViewWithSameColumnsAsBaseTable() throws 
Exception {
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW1", null, 
"VIEW1", ColumnDiff.EQUAL);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW", null, 
"VIEW2",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW3", "VIEWSCHEMA", 
"VIEW3",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW4", 
"VIEWSCHEMA", "VIEW4",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(true, TENANT_ID, "SAMESCHEMA", "TABLEWITHVIEW5", 
"SAMESCHEMA", "VIEW5",
+            ColumnDiff.EQUAL);
+    }
+
+    @Test
+    public void testUpgradeForTenantViewWithMoreColumnsThanBaseTable() throws 
Exception {
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW1", null, 
"VIEW1", ColumnDiff.MORE);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW", null, 
"VIEW2",
+            ColumnDiff.MORE);
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW3", "VIEWSCHEMA", 
"VIEW3",
+            ColumnDiff.MORE);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW4", 
"VIEWSCHEMA", "VIEW4",
+            ColumnDiff.MORE);
+        testViewUpgrade(true, TENANT_ID, "SAMESCHEMA", "TABLEWITHVIEW5", 
"SAMESCHEMA", "VIEW5",
+            ColumnDiff.MORE);
+    }
+
+    @Test
+    public void testUpgradeForViewWithSameColumnsAsBaseTable() throws 
Exception {
+        testViewUpgrade(false, null, null, "TABLEWITHVIEW1", null, "VIEW1", 
ColumnDiff.EQUAL);
+        testViewUpgrade(false, null, "TABLESCHEMA", "TABLEWITHVIEW", null, 
"VIEW2",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(false, null, null, "TABLEWITHVIEW3", "VIEWSCHEMA", 
"VIEW3",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(false, null, "TABLESCHEMA", "TABLEWITHVIEW4", 
"VIEWSCHEMA", "VIEW4",
+            ColumnDiff.EQUAL);
+        testViewUpgrade(false, null, "SAMESCHEMA", "TABLEWITHVIEW5", 
"SAMESCHEMA", "VIEW5",
+            ColumnDiff.EQUAL);
+    }
+
+    @Test
+    public void testUpgradeForViewWithMoreColumnsThanBaseTable() throws 
Exception {
+        testViewUpgrade(false, null, null, "TABLEWITHVIEW1", null, "VIEW1", 
ColumnDiff.MORE);
+        testViewUpgrade(false, null, "TABLESCHEMA", "TABLEWITHVIEW", null, 
"VIEW2", ColumnDiff.MORE);
+        testViewUpgrade(false, null, null, "TABLEWITHVIEW3", "VIEWSCHEMA", 
"VIEW3", ColumnDiff.MORE);
+        testViewUpgrade(false, null, "TABLESCHEMA", "TABLEWITHVIEW4", 
"VIEWSCHEMA", "VIEW4",
+            ColumnDiff.MORE);
+        testViewUpgrade(false, null, "SAMESCHEMA", "TABLEWITHVIEW5", 
"SAMESCHEMA", "VIEW5",
+            ColumnDiff.MORE);
+    }
+
+    @Test
+    public void testSettingBaseColumnCountWhenBaseTableColumnDropped() throws 
Exception {
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW1", null, 
"VIEW1", ColumnDiff.MORE);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW", null, 
"VIEW2",
+            ColumnDiff.LESS);
+        testViewUpgrade(true, TENANT_ID, null, "TABLEWITHVIEW3", "VIEWSCHEMA", 
"VIEW3",
+            ColumnDiff.LESS);
+        testViewUpgrade(true, TENANT_ID, "TABLESCHEMA", "TABLEWITHVIEW4", 
"VIEWSCHEMA", "VIEW4",
+            ColumnDiff.LESS);
+        testViewUpgrade(true, TENANT_ID, "SAMESCHEMA", "TABLEWITHVIEW5", 
"SAMESCHEMA", "VIEW5",
+            ColumnDiff.LESS);
+    }
+    
+    @Test
+    public void testSettingBaseColumnCountForMultipleViewsOnTable() throws 
Exception {
+        String baseSchema = "XYZ";
+        String baseTable = "BASE_TABLE";
+        String fullBaseTableName = SchemaUtil.getTableName(baseSchema, 
baseTable);
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            String baseTableDDL = "CREATE TABLE " + fullBaseTableName + " 
(TENANT_ID VARCHAR NOT NULL, PK1 VARCHAR NOT NULL, V1 INTEGER, V2 INTEGER 
CONSTRAINT NAME_PK PRIMARY KEY(TENANT_ID, PK1)) MULTI_TENANT = true";
+            conn.createStatement().execute(baseTableDDL);
+            
+            for (int i = 1; i <=2; i++) {
+                // Create views for tenants;
+                String tenant = "tenant" + i;
+                try (Connection tenantConn = createTenantConnection(tenant)) {
+                    String view = "TENANT_VIEW1";
+                    
+                    // view with its own column
+                    String viewDDL = "CREATE VIEW " + view + " AS SELECT * 
FROM " + fullBaseTableName;
+                    tenantConn.createStatement().execute(viewDDL);
+                    String addCols = "ALTER VIEW " + view + " ADD COL1 VARCHAR 
";
+                    tenantConn.createStatement().execute(addCols);
+                    removeBaseColumnCountKV(tenant, null, view);
+
+                    // view that has the last base table column removed
+                    view = "TENANT_VIEW2";
+                    viewDDL = "CREATE VIEW " + view + " AS SELECT * FROM " + 
fullBaseTableName;
+                    tenantConn.createStatement().execute(viewDDL);
+                    String droplastBaseCol = "ALTER VIEW " + view + " DROP 
COLUMN V2";
+                    tenantConn.createStatement().execute(droplastBaseCol);
+                    removeBaseColumnCountKV(tenant, null, view);
+
+                    // view that has the middle base table column removed
+                    view = "TENANT_VIEW3";
+                    viewDDL = "CREATE VIEW " + view + " AS SELECT * FROM " + 
fullBaseTableName;
+                    tenantConn.createStatement().execute(viewDDL);
+                    String dropMiddileBaseCol = "ALTER VIEW " + view + " DROP 
COLUMN V1";
+                    tenantConn.createStatement().execute(dropMiddileBaseCol);
+                    removeBaseColumnCountKV(tenant, null, view);
+                }
+            }
+            
+            // create global views
+            try (Connection globalConn = 
DriverManager.getConnection(getUrl())) {
+                String view = "GLOBAL_VIEW1";
+                
+                // view with its own column
+                String viewDDL = "CREATE VIEW " + view + " AS SELECT * FROM " 
+ fullBaseTableName;
+                globalConn.createStatement().execute(viewDDL);
+                String addCols = "ALTER VIEW " + view + " ADD COL1 VARCHAR ";
+                globalConn.createStatement().execute(addCols);
+                removeBaseColumnCountKV(null, null, view);
+
+                // view that has the last base table column removed
+                view = "GLOBAL_VIEW2";
+                viewDDL = "CREATE VIEW " + view + " AS SELECT * FROM " + 
fullBaseTableName;
+                globalConn.createStatement().execute(viewDDL);
+                String droplastBaseCol = "ALTER VIEW " + view + " DROP COLUMN 
V2";
+                globalConn.createStatement().execute(droplastBaseCol);
+                removeBaseColumnCountKV(null, null, view);
+
+                // view that has the middle base table column removed
+                view = "GLOBAL_VIEW3";
+                viewDDL = "CREATE VIEW " + view + " AS SELECT * FROM " + 
fullBaseTableName;
+                globalConn.createStatement().execute(viewDDL);
+                String dropMiddileBaseCol = "ALTER VIEW " + view + " DROP 
COLUMN V1";
+                globalConn.createStatement().execute(dropMiddileBaseCol);
+                removeBaseColumnCountKV(null, null, view);
+            }
+            
+            // run upgrade
+            UpgradeUtil.upgradeTo4_5_0(conn.unwrap(PhoenixConnection.class));
+            
+            // Verify base column counts for tenant specific views
+            for (int i = 1; i <=2 ; i++) {
+                String tenantId = "tenant" + i;
+                checkBaseColumnCount(tenantId, null, "TENANT_VIEW1", 4);
+                checkBaseColumnCount(tenantId, null, "TENANT_VIEW2", 
DIVORCED_VIEW_BASE_COLUMN_COUNT);
+                checkBaseColumnCount(tenantId, null, "TENANT_VIEW3", 
DIVORCED_VIEW_BASE_COLUMN_COUNT);
+            }
+            
+            // Verify base column count for global views
+            checkBaseColumnCount(null, null, "GLOBAL_VIEW1", 4);
+            checkBaseColumnCount(null, null, "GLOBAL_VIEW2", 
DIVORCED_VIEW_BASE_COLUMN_COUNT);
+            checkBaseColumnCount(null, null, "GLOBAL_VIEW3", 
DIVORCED_VIEW_BASE_COLUMN_COUNT);
+        }
+        
+        
+    }
+    
+    private enum ColumnDiff {
+        MORE, EQUAL, LESS
+    };
+
+    private void testViewUpgrade(boolean tenantView, String tenantId, String 
baseTableSchema,
+            String baseTableName, String viewSchema, String viewName, 
ColumnDiff diff)
+            throws Exception {
+        if (tenantView) {
+            checkNotNull(tenantId);
+        } else {
+            checkArgument(tenantId == null);
+        }
+        Connection conn = DriverManager.getConnection(getUrl());
+        String fullViewName = SchemaUtil.getTableName(viewSchema, viewName);
+        String fullBaseTableName = SchemaUtil.getTableName(baseTableSchema, 
baseTableName);
+        try {
+            int expectedBaseColumnCount;
+            conn.createStatement().execute(
+                "CREATE TABLE IF NOT EXISTS " + fullBaseTableName + " ("
+                        + " TENANT_ID CHAR(15) NOT NULL, " + " PK1 integer NOT 
NULL, "
+                        + "PK2 bigint NOT NULL, " + "CF1.V1 VARCHAR, " + 
"CF2.V2 VARCHAR, "
+                        + "V3 CHAR(100) ARRAY[4] "
+                        + " CONSTRAINT NAME_PK PRIMARY KEY (TENANT_ID, PK1, 
PK2)"
+                        + " ) MULTI_TENANT= true");
+            
+            // create a view with same columns as base table.
+            try (Connection conn2 = getConnection(tenantView, tenantId)) {
+                conn2.createStatement().execute(
+                    "CREATE VIEW " + fullViewName + " AS SELECT * FROM " + 
fullBaseTableName);
+            }
+
+            if (diff == ColumnDiff.MORE) {
+                    // add a column to the view
+                    try (Connection conn3 = getConnection(tenantView, 
tenantId)) {
+                        conn3.createStatement().execute(
+                            "ALTER VIEW " + fullViewName + " ADD VIEW_COL1 
VARCHAR");
+                    }
+            }
+            if (diff == ColumnDiff.LESS) {
+                try (Connection conn3 = getConnection(tenantView, tenantId)) {
+                    conn3.createStatement().execute(
+                        "ALTER VIEW " + fullViewName + " DROP COLUMN CF2.V2");
+                }
+                expectedBaseColumnCount = DIVORCED_VIEW_BASE_COLUMN_COUNT;
+            } else {
+                expectedBaseColumnCount = 6;
+            }
+
+            checkBaseColumnCount(tenantId, viewSchema, viewName, 
expectedBaseColumnCount);
+            checkBaseColumnCount(null, baseTableSchema, baseTableName, 
BASE_TABLE_BASE_COLUMN_COUNT);
+            
+            // remove base column count kv so we can check whether the upgrade 
code is setting the 
+            // base column count correctly.
+            removeBaseColumnCountKV(tenantId, viewSchema, viewName);
+            removeBaseColumnCountKV(null, baseTableSchema, baseTableName);
+
+            // assert that the removing base column count key value worked 
correctly.
+            checkBaseColumnCount(tenantId, viewSchema, viewName, 0);
+            checkBaseColumnCount(null, baseTableSchema, baseTableName, 0);
+            
+            // run upgrade
+            UpgradeUtil.upgradeTo4_5_0(conn.unwrap(PhoenixConnection.class));
+
+            checkBaseColumnCount(tenantId, viewSchema, viewName, 
expectedBaseColumnCount);
+            checkBaseColumnCount(null, baseTableSchema, baseTableName, 
BASE_TABLE_BASE_COLUMN_COUNT);
+        } finally {
+            conn.close();
+        }
+    }
+
+    private static void checkBaseColumnCount(String tenantId, String 
schemaName, String tableName,
+            int expectedBaseColumnCount) throws Exception {
+        checkNotNull(tableName);
+        Connection conn = DriverManager.getConnection(getUrl());
+        String sql = SELECT_BASE_COLUMN_COUNT_FROM_HEADER_ROW;
+        sql =
+                String.format(sql, tenantId == null ? " IS NULL " : " = ? ",
+                    schemaName == null ? "IS NULL" : " = ? ");
+        int paramIndex = 1;
+        PreparedStatement stmt = conn.prepareStatement(sql);
+        if (tenantId != null) {
+            stmt.setString(paramIndex++, tenantId);
+        }
+        if (schemaName != null) {
+            stmt.setString(paramIndex++, schemaName);
+        }
+        stmt.setString(paramIndex, tableName);
+        ResultSet rs = stmt.executeQuery();
+        assertTrue(rs.next());
+        assertEquals(expectedBaseColumnCount, rs.getInt(1));
+        assertFalse(rs.next());
+    }
+
+    private static void
+            removeBaseColumnCountKV(String tenantId, String schemaName, String 
tableName)
+                    throws Exception {
+        byte[] rowKey =
+                SchemaUtil.getTableKey(tenantId == null ? new byte[0] : 
Bytes.toBytes(tenantId),
+                    schemaName == null ? new byte[0] : 
Bytes.toBytes(schemaName),
+                    Bytes.toBytes(tableName));
+        Put viewColumnDefinitionPut = new Put(rowKey, 
HConstants.LATEST_TIMESTAMP);
+        viewColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+            PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES, 
HConstants.LATEST_TIMESTAMP, null);
+
+        try (PhoenixConnection conn =
+                
(DriverManager.getConnection(getUrl())).unwrap(PhoenixConnection.class)) {
+            try (HTableInterface htable =
+                    conn.getQueryServices().getTable(
+                        
Bytes.toBytes(PhoenixDatabaseMetaData.SYSTEM_CATALOG_NAME))) {
+                RowMutations mutations = new RowMutations(rowKey);
+                mutations.add(viewColumnDefinitionPut);
+                htable.mutateRow(mutations);
+            }
+        }
+    }
+
+    private Connection createTenantConnection(String tenantId) throws 
SQLException {
+        Properties props = new Properties();
+        props.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
+        return DriverManager.getConnection(getUrl(), props);
+    }
+
+    private Connection getConnection(boolean tenantSpecific, String tenantId) 
throws SQLException {
+        if (tenantSpecific) {
+            checkNotNull(tenantId);
+            return createTenantConnection(tenantId);
+        }
+        return DriverManager.getConnection(getUrl());
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
index 1d578f5..077e325 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.coprocessor;
 
+import static org.apache.hadoop.hbase.KeyValueUtil.createFirstOnRow;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.ARRAY_SIZE_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.CLASS_NAME_BYTES;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.COLUMN_COUNT_BYTES;
@@ -61,6 +62,7 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_CONSTANT_BYTE
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID_BYTES;
 import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT_BYTES;
 import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_TYPE_BYTES;
+import static 
org.apache.phoenix.query.QueryConstants.DIVORCED_VIEW_BASE_COLUMN_COUNT;
 import static org.apache.phoenix.schema.PTableType.INDEX;
 import static org.apache.phoenix.util.SchemaUtil.getVarCharLength;
 import static org.apache.phoenix.util.SchemaUtil.getVarChars;
@@ -77,6 +79,7 @@ import java.util.List;
 import java.util.Set;
 
 import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CellUtil;
 import org.apache.hadoop.hbase.Coprocessor;
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HConstants;
@@ -204,24 +207,26 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
     private static final Logger logger = 
LoggerFactory.getLogger(MetaDataEndpointImpl.class);
 
     // KeyValues for Table
-    private static final KeyValue TABLE_TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_TYPE_BYTES);
-    private static final KeyValue TABLE_SEQ_NUM_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_SEQ_NUM_BYTES);
-    private static final KeyValue COLUMN_COUNT_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_COUNT_BYTES);
-    private static final KeyValue SALT_BUCKETS_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
SALT_BUCKETS_BYTES);
-    private static final KeyValue PK_NAME_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
PK_NAME_BYTES);
-    private static final KeyValue DATA_TABLE_NAME_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DATA_TABLE_NAME_BYTES);
-    private static final KeyValue INDEX_STATE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_STATE_BYTES);
-    private static final KeyValue IMMUTABLE_ROWS_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IMMUTABLE_ROWS_BYTES);
-    private static final KeyValue VIEW_EXPRESSION_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_STATEMENT_BYTES);
-    private static final KeyValue DEFAULT_COLUMN_FAMILY_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DEFAULT_COLUMN_FAMILY_NAME_BYTES);
-    private static final KeyValue DISABLE_WAL_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DISABLE_WAL_BYTES);
-    private static final KeyValue MULTI_TENANT_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MULTI_TENANT_BYTES);
-    private static final KeyValue VIEW_TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_TYPE_BYTES);
-    private static final KeyValue VIEW_INDEX_ID_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_INDEX_ID_BYTES);
-    private static final KeyValue INDEX_TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_TYPE_BYTES);
-    private static final KeyValue INDEX_DISABLE_TIMESTAMP_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_DISABLE_TIMESTAMP_BYTES);
-    private static final KeyValue STORE_NULLS_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
STORE_NULLS_BYTES);
-    private static final KeyValue EMPTY_KEYVALUE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
QueryConstants.EMPTY_COLUMN_BYTES);
+    private static final KeyValue TABLE_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_TYPE_BYTES);
+    private static final KeyValue TABLE_SEQ_NUM_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TABLE_SEQ_NUM_BYTES);
+    private static final KeyValue COLUMN_COUNT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_COUNT_BYTES);
+    private static final KeyValue SALT_BUCKETS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
SALT_BUCKETS_BYTES);
+    private static final KeyValue PK_NAME_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, PK_NAME_BYTES);
+    private static final KeyValue DATA_TABLE_NAME_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DATA_TABLE_NAME_BYTES);
+    private static final KeyValue INDEX_STATE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_STATE_BYTES);
+    private static final KeyValue IMMUTABLE_ROWS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IMMUTABLE_ROWS_BYTES);
+    private static final KeyValue VIEW_EXPRESSION_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_STATEMENT_BYTES);
+    private static final KeyValue DEFAULT_COLUMN_FAMILY_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DEFAULT_COLUMN_FAMILY_NAME_BYTES);
+    private static final KeyValue DISABLE_WAL_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DISABLE_WAL_BYTES);
+    private static final KeyValue MULTI_TENANT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MULTI_TENANT_BYTES);
+    private static final KeyValue VIEW_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_TYPE_BYTES);
+    private static final KeyValue VIEW_INDEX_ID_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_INDEX_ID_BYTES);
+    private static final KeyValue INDEX_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_TYPE_BYTES);
+    private static final KeyValue INDEX_DISABLE_TIMESTAMP_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
INDEX_DISABLE_TIMESTAMP_BYTES);
+    private static final KeyValue STORE_NULLS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
STORE_NULLS_BYTES);
+    private static final KeyValue EMPTY_KEYVALUE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
QueryConstants.EMPTY_COLUMN_BYTES);
+    private static final KeyValue BASE_COLUMN_COUNT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES);
+    
     private static final List<KeyValue> TABLE_KV_COLUMNS = 
Arrays.<KeyValue>asList(
             EMPTY_KEYVALUE_KV,
             TABLE_TYPE_KV,
@@ -240,7 +245,8 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
             VIEW_INDEX_ID_KV,
             INDEX_TYPE_KV,
             INDEX_DISABLE_TIMESTAMP_KV,
-            STORE_NULLS_KV
+            STORE_NULLS_KV,
+            BASE_COLUMN_COUNT_KV
             );
     static {
         Collections.sort(TABLE_KV_COLUMNS, KeyValue.COMPARATOR);
@@ -262,18 +268,19 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
     private static final int VIEW_INDEX_ID_INDEX = 
TABLE_KV_COLUMNS.indexOf(VIEW_INDEX_ID_KV);
     private static final int INDEX_TYPE_INDEX = 
TABLE_KV_COLUMNS.indexOf(INDEX_TYPE_KV);
     private static final int STORE_NULLS_INDEX = 
TABLE_KV_COLUMNS.indexOf(STORE_NULLS_KV);
+    private static final int BASE_COLUMN_COUNT_INDEX = 
TABLE_KV_COLUMNS.indexOf(BASE_COLUMN_COUNT_KV);
 
     // KeyValues for Column
-    private static final KeyValue DECIMAL_DIGITS_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DECIMAL_DIGITS_BYTES);
-    private static final KeyValue COLUMN_SIZE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_SIZE_BYTES);
-    private static final KeyValue NULLABLE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
NULLABLE_BYTES);
-    private static final KeyValue DATA_TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DATA_TYPE_BYTES);
-    private static final KeyValue ORDINAL_POSITION_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ORDINAL_POSITION_BYTES);
-    private static final KeyValue SORT_ORDER_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
SORT_ORDER_BYTES);
-    private static final KeyValue ARRAY_SIZE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ARRAY_SIZE_BYTES);
-    private static final KeyValue VIEW_CONSTANT_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_CONSTANT_BYTES);
-    private static final KeyValue IS_VIEW_REFERENCED_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IS_VIEW_REFERENCED_BYTES);
-    private static final KeyValue COLUMN_DEF_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_DEF_BYTES);
+    private static final KeyValue DECIMAL_DIGITS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DECIMAL_DIGITS_BYTES);
+    private static final KeyValue COLUMN_SIZE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_SIZE_BYTES);
+    private static final KeyValue NULLABLE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, NULLABLE_BYTES);
+    private static final KeyValue DATA_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DATA_TYPE_BYTES);
+    private static final KeyValue ORDINAL_POSITION_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ORDINAL_POSITION_BYTES);
+    private static final KeyValue SORT_ORDER_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
SORT_ORDER_BYTES);
+    private static final KeyValue ARRAY_SIZE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
ARRAY_SIZE_BYTES);
+    private static final KeyValue VIEW_CONSTANT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
VIEW_CONSTANT_BYTES);
+    private static final KeyValue IS_VIEW_REFERENCED_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IS_VIEW_REFERENCED_BYTES);
+    private static final KeyValue COLUMN_DEF_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
COLUMN_DEF_BYTES);
     private static final List<KeyValue> COLUMN_KV_COLUMNS = 
Arrays.<KeyValue>asList(
             DECIMAL_DIGITS_KV,
             COLUMN_SIZE_KV,
@@ -303,16 +310,16 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
     
     private static final int LINK_TYPE_INDEX = 0;
 
-    private static final KeyValue CLASS_NAME_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
CLASS_NAME_BYTES);
-    private static final KeyValue JAR_PATH_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
JAR_PATH_BYTES);
-    private static final KeyValue RETURN_TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
RETURN_TYPE_BYTES);
-    private static final KeyValue NUM_ARGS_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
NUM_ARGS_BYTES);
-    private static final KeyValue TYPE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
TYPE_BYTES);
-    private static final KeyValue IS_CONSTANT_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IS_CONSTANT_BYTES);
-    private static final KeyValue DEFAULT_VALUE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DEFAULT_VALUE_BYTES);
-    private static final KeyValue MIN_VALUE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MIN_VALUE_BYTES);
-    private static final KeyValue MAX_VALUE_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MAX_VALUE_BYTES);
-    private static final KeyValue IS_ARRAY_KV = 
KeyValue.createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IS_ARRAY_BYTES);
+    private static final KeyValue CLASS_NAME_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
CLASS_NAME_BYTES);
+    private static final KeyValue JAR_PATH_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, JAR_PATH_BYTES);
+    private static final KeyValue RETURN_TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
RETURN_TYPE_BYTES);
+    private static final KeyValue NUM_ARGS_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, NUM_ARGS_BYTES);
+    private static final KeyValue TYPE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, TYPE_BYTES);
+    private static final KeyValue IS_CONSTANT_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
IS_CONSTANT_BYTES);
+    private static final KeyValue DEFAULT_VALUE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
DEFAULT_VALUE_BYTES);
+    private static final KeyValue MIN_VALUE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MIN_VALUE_BYTES);
+    private static final KeyValue MAX_VALUE_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, 
MAX_VALUE_BYTES);
+    private static final KeyValue IS_ARRAY_KV = 
createFirstOnRow(ByteUtil.EMPTY_BYTE_ARRAY, TABLE_FAMILY_BYTES, IS_ARRAY_BYTES);
 
     private static final List<KeyValue> FUNCTION_KV_COLUMNS = 
Arrays.<KeyValue>asList(
         EMPTY_KEYVALUE_KV,
@@ -400,7 +407,6 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         byte[] tableName = request.getTableName().toByteArray();
         byte[] key = SchemaUtil.getTableKey(tenantId, schemaName, tableName);
         long tableTimeStamp = request.getTableTimestamp();
-
         try {
             // TODO: check that key is within region.getStartKey() and 
region.getEndKey()
             // and return special code to force client to lookup region from 
meta.
@@ -766,7 +772,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         Short viewIndexId = viewIndexIdKv == null ? null : 
(Short)MetaDataUtil.getViewIndexIdDataType().getCodec().decodeShort(viewIndexIdKv.getValueArray(),
 viewIndexIdKv.getValueOffset(), SortOrder.getDefault());
         Cell indexTypeKv = tableKeyValues[INDEX_TYPE_INDEX];
         IndexType indexType = indexTypeKv == null ? null : 
IndexType.fromSerializedValue(indexTypeKv.getValueArray()[indexTypeKv.getValueOffset()]);
-
+        Cell baseColumnCountKv = tableKeyValues[BASE_COLUMN_COUNT_INDEX];
+        int baseColumnCount = baseColumnCountKv == null ? 0 : 
PInteger.INSTANCE.getCodec().decodeInt(baseColumnCountKv.getValueArray(),
+            baseColumnCountKv.getValueOffset(), SortOrder.getDefault());
 
         List<PColumn> columns = 
Lists.newArrayListWithExpectedSize(columnCount);
         List<PTable> indexes = new ArrayList<PTable>();
@@ -811,7 +819,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         return PTableImpl.makePTable(tenantId, schemaName, tableName, 
tableType, indexState, timeStamp,
             tableSeqNum, pkName, saltBucketNum, columns, tableType == INDEX ? 
schemaName : null,
             tableType == INDEX ? dataTableName : null, indexes, 
isImmutableRows, physicalTables, defaultFamilyName, viewStatement,
-            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, stats);
+            disableWAL, multiTenant, storeNulls, viewType, viewIndexId, 
indexType, stats, baseColumnCount);
     }
 
     private PFunction getFunction(RegionScanner scanner)
@@ -1161,14 +1169,16 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
         locks.add(rowLock);
     }
 
-    protected static final byte[] PHYSICAL_TABLE_BYTES = new byte[] 
{PTable.LinkType.PHYSICAL_TABLE.getSerializedValue()};
+    private static final byte[] PHYSICAL_TABLE_BYTES = new byte[] 
{PTable.LinkType.PHYSICAL_TABLE.getSerializedValue()};
+    private static final byte[] PARENT_TABLE_BYTES = new byte[] 
{PTable.LinkType.PARENT_TABLE.getSerializedValue()};
+
     /**
      * @param tableName parent table's name
      * Looks for whether child views exist for the table specified by table.
      * TODO: should we pass a timestamp here?
      */
     @SuppressWarnings("deprecation")
-    private TableViewFinderResult findChildViews(Region region, byte[] 
tenantId, PTable table) throws IOException {
+    private TableViewFinderResult findChildViews(Region region, byte[] 
tenantId, PTable table, byte[] linkTypeBytes) throws IOException {
         byte[] schemaName = table.getSchemaName().getBytes();
         byte[] tableName = table.getTableName().getBytes();
         boolean isMultiTenant = table.isMultiTenant();
@@ -1182,13 +1192,15 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             scan.setStartRow(startRow);
             scan.setStopRow(stopRow);
         }
-        SingleColumnValueFilter linkFilter = new 
SingleColumnValueFilter(TABLE_FAMILY_BYTES, LINK_TYPE_BYTES, CompareOp.EQUAL, 
PHYSICAL_TABLE_BYTES);
+        SingleColumnValueFilter linkFilter = new 
SingleColumnValueFilter(TABLE_FAMILY_BYTES, LINK_TYPE_BYTES, CompareOp.EQUAL, 
linkTypeBytes);
         linkFilter.setFilterIfMissing(true);
         byte[] suffix = ByteUtil.concat(QueryConstants.SEPARATOR_BYTE_ARRAY, 
SchemaUtil.getTableNameAsBytes(schemaName, tableName));
         SuffixFilter rowFilter = new SuffixFilter(suffix);
         Filter filter = new FilterList(linkFilter, rowFilter);
         scan.setFilter(filter);
         scan.addColumn(TABLE_FAMILY_BYTES, LINK_TYPE_BYTES);
+        scan.addColumn(TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
+        
         // Original region-only scanner modified due to PHOENIX-1208
         // RegionScanner scanner = region.getScanner(scan);
         // The following *should* work, but doesn't due to HBASE-11837
@@ -1354,7 +1366,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
           }
 
           // Handle any child views that exist
-          TableViewFinderResult tableViewFinderResult = findChildViews(region, 
tenantId, table);
+          TableViewFinderResult tableViewFinderResult = findChildViews(region, 
tenantId, table, PHYSICAL_TABLE_BYTES);
           if (tableViewFinderResult.hasViews()) {
             if (isCascade) {
               if (tableViewFinderResult.allViewsInMultipleRegions()) {
@@ -1438,7 +1450,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
     private static interface ColumnMutator {
       MetaDataMutationResult updateMutation(PTable table, byte[][] 
rowKeyMetaData,
           List<Mutation> tableMetadata, Region region,
-          List<ImmutableBytesPtr> invalidateList, List<RowLock> locks) throws 
IOException,
+          List<ImmutableBytesPtr> invalidateList, List<RowLock> locks, long 
clientTimeStamp) throws IOException,
           SQLException;
     }
 
@@ -1521,24 +1533,24 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                             EnvironmentEdgeManager.currentTimeMillis(), null);
                 } else {
                     // server-side, except for indexing, we always expect the 
keyvalues to be standard KeyValues
-                    PTableType expectedType = 
MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE, new 
ImmutableBytesPtr());
+                    PTableType expectedType = 
MetaDataUtil.getTableType(tableMetadata, GenericKeyValueBuilder.INSTANCE,
+                            new ImmutableBytesPtr());
                     // We said to drop a table, but found a view or visa versa
-                    if (type != expectedType) {
-                        return new 
MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND, 
EnvironmentEdgeManager.currentTimeMillis(), null);
-                    }
-                    if (findChildViews(region, tenantId, table).hasViews()) {
-                        // Disallow any column mutations for parents of tenant 
tables
-                        return new 
MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, 
EnvironmentEdgeManager.currentTimeMillis(), null);
+                    if (type != expectedType) { return new 
MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND,
+                            EnvironmentEdgeManager.currentTimeMillis(), null); 
}
+                    if (table.getBaseColumnCount() == 0) {
+                        // If the base column count hasn't been set, then it 
means that the upgrade
+                        // to 4.5.0 is in progress. Have the client retry the 
mutation operation.
+                        return new 
MetaDataMutationResult(MutationCode.CONCURRENT_TABLE_MUTATION,
+                                EnvironmentEdgeManager.currentTimeMillis(), 
table);
                     }
                 }
                 result = mutator.updateMutation(table, rowKeyMetaData, 
tableMetadata, region,
-                            invalidateList, locks);
+                            invalidateList, locks, clientTimeStamp);
                 if (result != null) {
                     return result;
                 }
-
-                region.mutateRowsWithLocks(tableMetadata, Collections.<byte[]> 
emptySet(), HConstants.NO_NONCE,
-                    HConstants.NO_NONCE);
+                region.mutateRowsWithLocks(tableMetadata, Collections.<byte[]> 
emptySet(), HConstants.NO_NONCE, HConstants.NO_NONCE);
                 // Invalidate from cache
                 for (ImmutableBytesPtr invalidateKey : invalidateList) {
                     metaDataCache.invalidate(invalidateKey);
@@ -1557,6 +1569,85 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
         }
     }
 
+    private void addRowsToChildViews(List<Mutation> tableMetadata, 
List<Mutation> mutationsForAddingColumnsToViews, byte[] schemaName, byte[] 
tableName,
+            List<ImmutableBytesPtr> invalidateList, long clientTimeStamp, 
TableViewFinderResult childViewsResult,
+            Region region, List<RowLock> locks) throws IOException, 
SQLException {
+        for (Result viewResult : childViewsResult.getResults()) {
+            byte[][] rowViewKeyMetaData = new byte[3][];
+            getVarChars(viewResult.getRow(), 3, rowViewKeyMetaData);
+            byte[] viewTenantId = 
rowViewKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
+            byte[] viewSchemaName = 
rowViewKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
+            byte[] viewName = 
rowViewKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
+            byte[] viewKey = SchemaUtil.getTableKey(viewTenantId, 
viewSchemaName, viewName);
+            PTable view = doGetTable(viewKey, clientTimeStamp);
+
+            if (view.getBaseColumnCount() == 
QueryConstants.DIVORCED_VIEW_BASE_COLUMN_COUNT) {
+                // if a view has divorced itself from the base table, we don't 
allow schema changes
+                // to be propagated to it.
+                return;
+            }
+            // lock the rows corresponding to views so that no other thread 
can modify the view meta-data
+            acquireLock(region, viewKey, locks);
+
+            int deltaNumberOfColumns = 0;
+            
+            for (Mutation m : tableMetadata) {
+                byte[][] rkmd = new byte[5][];
+                int pkCount = getVarChars(m.getRow(), rkmd);
+                if (m instanceof Put && pkCount > COLUMN_NAME_INDEX
+                        && Bytes.compareTo(schemaName, 
rkmd[SCHEMA_NAME_INDEX]) == 0
+                        && Bytes.compareTo(tableName, rkmd[TABLE_NAME_INDEX]) 
== 0) {
+                    Put p = (Put)m;
+
+                    byte[] k = ByteUtil.concat(viewKey, 
QueryConstants.SEPARATOR_BYTE_ARRAY, rkmd[COLUMN_NAME_INDEX],
+                            QueryConstants.SEPARATOR_BYTE_ARRAY, 
rkmd[FAMILY_NAME_INDEX]);
+                    Put viewColumnDefinitionPut = new Put(k, clientTimeStamp);
+                    for (Cell cell : 
p.getFamilyCellMap().values().iterator().next()) {
+                        viewColumnDefinitionPut.add(CellUtil.createCell(k, 
CellUtil.cloneFamily(cell),
+                                CellUtil.cloneQualifier(cell), 
cell.getTimestamp(), cell.getTypeByte(),
+                                CellUtil.cloneValue(cell)));
+                    }
+                    deltaNumberOfColumns++;
+                    
mutationsForAddingColumnsToViews.add(viewColumnDefinitionPut);
+                }
+            }
+
+            int oldBaseColumnCount = view.getBaseColumnCount();
+
+            Put viewHeaderRowPut = new Put(viewKey, clientTimeStamp);
+            byte[] baseColumnCountPtr = new 
byte[PInteger.INSTANCE.getByteSize()];
+            PInteger.INSTANCE.getCodec().encodeInt(oldBaseColumnCount + 
deltaNumberOfColumns, baseColumnCountPtr, 0);
+            byte[] columnCountPtr = new byte[PInteger.INSTANCE.getByteSize()];
+            PInteger.INSTANCE.getCodec().encodeInt(view.getColumns().size() + 
deltaNumberOfColumns, columnCountPtr, 0);
+            byte[] viewSequencePtr = new byte[PLong.INSTANCE.getByteSize()];
+            PLong.INSTANCE.getCodec().encodeLong(view.getSequenceNumber() + 1, 
viewSequencePtr, 0);
+            
viewHeaderRowPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                    PhoenixDatabaseMetaData.COLUMN_COUNT_BYTES, 
clientTimeStamp, columnCountPtr);
+            
viewHeaderRowPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                    PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES, 
clientTimeStamp, baseColumnCountPtr);
+            
viewHeaderRowPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                    PhoenixDatabaseMetaData.TABLE_SEQ_NUM_BYTES, 
clientTimeStamp, viewSequencePtr);
+            mutationsForAddingColumnsToViews.add(viewHeaderRowPut);
+
+            // Update positions of view columns
+            for (PColumn column : view.getColumns()) {
+                if (column.getPosition() >= oldBaseColumnCount) {
+                    int newPosition = column.getPosition() + 
deltaNumberOfColumns + 1;
+
+                    byte[] k = ByteUtil.concat(viewKey, 
QueryConstants.SEPARATOR_BYTE_ARRAY, column.getName()
+                            .getBytes(), QueryConstants.SEPARATOR_BYTE_ARRAY, 
column.getFamilyName() != null ? column.getFamilyName().getBytes() : null);
+
+                    Put positionUpdatePut = new Put(k, clientTimeStamp);
+                    byte[] ptr = new byte[PInteger.INSTANCE.getByteSize()];
+                    PInteger.INSTANCE.getCodec().encodeInt(newPosition, ptr, 
0);
+                    
positionUpdatePut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                            PhoenixDatabaseMetaData.ORDINAL_POSITION_BYTES, 
clientTimeStamp, ptr);
+                    mutationsForAddingColumnsToViews.add(positionUpdatePut);
+                }
+            }
+            invalidateList.add(new ImmutableBytesPtr(viewKey));
+        }
+    }
 
     @Override
     public void addColumn(RpcController controller, AddColumnRequest request,
@@ -1566,11 +1657,29 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             MetaDataMutationResult result = mutateColumn(tableMetaData, new 
ColumnMutator() {
                 @Override
                 public MetaDataMutationResult updateMutation(PTable table, 
byte[][] rowKeyMetaData,
-                        List<Mutation> tableMetaData, Region region,
-                        List<ImmutableBytesPtr> invalidateList, List<RowLock> 
locks) {
+                        List<Mutation> tableMetaData, Region region, 
List<ImmutableBytesPtr> invalidateList,
+                        List<RowLock> locks, long clientTimeStamp) throws 
IOException, SQLException {
                     byte[] tenantId = rowKeyMetaData[TENANT_ID_INDEX];
                     byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX];
                     byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX];
+                    PTableType type = table.getType();
+                    TableViewFinderResult childViewsResult = 
findChildViews(region, tenantId, table,
+                            (type == PTableType.VIEW ? PARENT_TABLE_BYTES : 
PHYSICAL_TABLE_BYTES));
+                    List<Mutation> mutationsForAddingColumnsToViews = 
Collections.emptyList();
+                    if (childViewsResult.hasViews()) {
+                        /*
+                         * Adding a column is not allowed if: 1) Meta-data for 
child view/s spans over more than one
+                         * region. 2) Adding column to a views that has child 
view/s.
+                         */
+                        if (!childViewsResult.allViewsInSingleRegion() || type 
== PTableType.VIEW) {
+                            return new 
MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION,
+                                    
EnvironmentEdgeManager.currentTimeMillis(), null);
+                        } else {
+                            mutationsForAddingColumnsToViews = new 
ArrayList<>(childViewsResult.getResults().size() * tableMetaData.size());
+                            addRowsToChildViews(tableMetaData, 
mutationsForAddingColumnsToViews, schemaName, tableName, invalidateList, 
clientTimeStamp,
+                                    childViewsResult, region, locks);
+                        }
+                    }
                     for (Mutation m : tableMetaData) {
                         byte[] key = m.getRow();
                         boolean addingPKColumn = false;
@@ -1613,6 +1722,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                             }
                         }
                     }
+                    tableMetaData.addAll(mutationsForAddingColumnsToViews);
                     return null;
                 }
             });
@@ -1754,19 +1864,27 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
 
         try {
             tableMetaData = ProtobufUtil.getMutations(request);
-            final long clientTimeStamp = 
MetaDataUtil.getClientTimeStamp(tableMetaData);
             final List<byte[]> tableNamesToDelete = Lists.newArrayList();
             MetaDataMutationResult result = mutateColumn(tableMetaData, new 
ColumnMutator() {
                 @Override
                 public MetaDataMutationResult updateMutation(PTable table, 
byte[][] rowKeyMetaData,
                         List<Mutation> tableMetaData, Region region,
-                        List<ImmutableBytesPtr> invalidateList, List<RowLock> 
locks)
+                        List<ImmutableBytesPtr> invalidateList, List<RowLock> 
locks, long clientTimeStamp)
                         throws IOException, SQLException {
                     byte[] tenantId = rowKeyMetaData[TENANT_ID_INDEX];
                     byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX];
                     byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX];
                     boolean deletePKColumn = false;
                     List<Mutation> additionalTableMetaData = 
Lists.newArrayList();
+                    
+                    PTableType type = table.getType();
+                    TableViewFinderResult childViewsResult = 
findChildViews(region, tenantId, table,
+                            (type == PTableType.VIEW ? PARENT_TABLE_BYTES : 
PHYSICAL_TABLE_BYTES));
+                    if (childViewsResult.hasViews()) {
+                        return new 
MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, 
EnvironmentEdgeManager
+                                .currentTimeMillis(), null);
+                    }
+                    
                     for (Mutation m : tableMetaData) {
                         if (m instanceof Delete) {
                             byte[] key = m.getRow();
@@ -1791,6 +1909,26 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                                     } else {
                                         continue;
                                     }
+                                    if (table.getType() == PTableType.VIEW) {
+                                        if (table.getBaseColumnCount() != 
DIVORCED_VIEW_BASE_COLUMN_COUNT
+                                                && 
columnToDelete.getPosition() < table.getBaseColumnCount()) {
+                                            /*
+                                             * If the column being dropped is 
inherited from the base table, then the
+                                             * view is about to divorce itself 
from the base table. Divorce here means
+                                             * that any further meta-data 
changes made to the base table will not be
+                                             * propagated to the hierarchy of 
views on the base table.
+                                             */
+                                            byte[] viewKey = 
SchemaUtil.getTableKey(tenantId, schemaName, tableName);
+                                            Put updateBaseColumnCountPut = new 
Put(viewKey);
+                                            byte[] baseColumnCountPtr = new 
byte[PInteger.INSTANCE.getByteSize()];
+                                            
PInteger.INSTANCE.getCodec().encodeInt(DIVORCED_VIEW_BASE_COLUMN_COUNT,
+                                                    baseColumnCountPtr, 0);
+                                            
updateBaseColumnCountPut.addColumn(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                                    
PhoenixDatabaseMetaData.BASE_COLUMN_COUNT_BYTES, clientTimeStamp,
+                                                    baseColumnCountPtr);
+                                            
additionalTableMetaData.add(updateBaseColumnCountPut);
+                                        }
+                                    }
                                     if (columnToDelete.isViewReferenced()) { 
// Disallow deletion of column referenced in WHERE clause of view
                                         return new 
MetaDataMutationResult(MutationCode.UNALLOWED_TABLE_MUTATION, 
EnvironmentEdgeManager.currentTimeMillis(), table, columnToDelete);
                                     }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
index 3867e00..9009e7c 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataProtocol.java
@@ -62,8 +62,7 @@ public abstract class MetaDataProtocol extends 
MetaDataService {
 
     public static final long MIN_TABLE_TIMESTAMP = 0;
 
-    // Incremented from 5 to 7 with the addition of the STORE_NULLS table 
option in 4.3
-    public static final long MIN_SYSTEM_TABLE_TIMESTAMP = MIN_TABLE_TIMESTAMP 
+ 7;
+    public static final long MIN_SYSTEM_TABLE_TIMESTAMP = MIN_TABLE_TIMESTAMP 
+ 8;
     public static final int DEFAULT_MAX_META_DATA_VERSIONS = 1000;
     public static final int DEFAULT_MAX_STAT_DATA_VERSIONS = 3;
     public static final boolean DEFAULT_META_DATA_KEEP_DELETED_CELLS = true;
@@ -73,6 +72,7 @@ public abstract class MetaDataProtocol extends 
MetaDataService {
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_2_0 = 
MIN_TABLE_TIMESTAMP + 4;
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_2_1 = 
MIN_TABLE_TIMESTAMP + 5;
     public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_3_0 = 
MIN_TABLE_TIMESTAMP + 7;
+    public static final long MIN_SYSTEM_TABLE_TIMESTAMP_4_5_0 = 
MIN_TABLE_TIMESTAMP + 8;
     
     // TODO: pare this down to minimum, as we don't need duplicates for both 
table and column errors, nor should we need
     // a different code for every type of error.

http://git-wip-us.apache.org/repos/asf/phoenix/blob/e78eb6fa/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
index 7d389ac..dd6e303 100644
--- 
a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
+++ 
b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/generated/PTableProtos.java
@@ -3108,6 +3108,16 @@ public final class PTableProtos {
      * <code>optional bool storeNulls = 24;</code>
      */
     boolean getStoreNulls();
+
+    // optional int32 baseColumnCount = 25;
+    /**
+     * <code>optional int32 baseColumnCount = 25;</code>
+     */
+    boolean hasBaseColumnCount();
+    /**
+     * <code>optional int32 baseColumnCount = 25;</code>
+     */
+    int getBaseColumnCount();
   }
   /**
    * Protobuf type {@code PTable}
@@ -3298,6 +3308,11 @@ public final class PTableProtos {
               storeNulls_ = input.readBool();
               break;
             }
+            case 200: {
+              bitField0_ |= 0x00100000;
+              baseColumnCount_ = input.readInt32();
+              break;
+            }
           }
         }
       } catch (com.google.protobuf.InvalidProtocolBufferException e) {
@@ -3828,6 +3843,22 @@ public final class PTableProtos {
       return storeNulls_;
     }
 
+    // optional int32 baseColumnCount = 25;
+    public static final int BASECOLUMNCOUNT_FIELD_NUMBER = 25;
+    private int baseColumnCount_;
+    /**
+     * <code>optional int32 baseColumnCount = 25;</code>
+     */
+    public boolean hasBaseColumnCount() {
+      return ((bitField0_ & 0x00100000) == 0x00100000);
+    }
+    /**
+     * <code>optional int32 baseColumnCount = 25;</code>
+     */
+    public int getBaseColumnCount() {
+      return baseColumnCount_;
+    }
+
     private void initFields() {
       schemaNameBytes_ = com.google.protobuf.ByteString.EMPTY;
       tableNameBytes_ = com.google.protobuf.ByteString.EMPTY;
@@ -3853,6 +3884,7 @@ public final class PTableProtos {
       indexType_ = com.google.protobuf.ByteString.EMPTY;
       statsTimeStamp_ = 0L;
       storeNulls_ = false;
+      baseColumnCount_ = 0;
     }
     private byte memoizedIsInitialized = -1;
     public final boolean isInitialized() {
@@ -3992,6 +4024,9 @@ public final class PTableProtos {
       if (((bitField0_ & 0x00080000) == 0x00080000)) {
         output.writeBool(24, storeNulls_);
       }
+      if (((bitField0_ & 0x00100000) == 0x00100000)) {
+        output.writeInt32(25, baseColumnCount_);
+      }
       getUnknownFields().writeTo(output);
     }
 
@@ -4102,6 +4137,10 @@ public final class PTableProtos {
         size += com.google.protobuf.CodedOutputStream
           .computeBoolSize(24, storeNulls_);
       }
+      if (((bitField0_ & 0x00100000) == 0x00100000)) {
+        size += com.google.protobuf.CodedOutputStream
+          .computeInt32Size(25, baseColumnCount_);
+      }
       size += getUnknownFields().getSerializedSize();
       memoizedSerializedSize = size;
       return size;
@@ -4233,6 +4272,11 @@ public final class PTableProtos {
         result = result && (getStoreNulls()
             == other.getStoreNulls());
       }
+      result = result && (hasBaseColumnCount() == other.hasBaseColumnCount());
+      if (hasBaseColumnCount()) {
+        result = result && (getBaseColumnCount()
+            == other.getBaseColumnCount());
+      }
       result = result &&
           getUnknownFields().equals(other.getUnknownFields());
       return result;
@@ -4342,6 +4386,10 @@ public final class PTableProtos {
         hash = (37 * hash) + STORENULLS_FIELD_NUMBER;
         hash = (53 * hash) + hashBoolean(getStoreNulls());
       }
+      if (hasBaseColumnCount()) {
+        hash = (37 * hash) + BASECOLUMNCOUNT_FIELD_NUMBER;
+        hash = (53 * hash) + getBaseColumnCount();
+      }
       hash = (29 * hash) + getUnknownFields().hashCode();
       memoizedHashCode = hash;
       return hash;
@@ -4514,6 +4562,8 @@ public final class PTableProtos {
         bitField0_ = (bitField0_ & ~0x00400000);
         storeNulls_ = false;
         bitField0_ = (bitField0_ & ~0x00800000);
+        baseColumnCount_ = 0;
+        bitField0_ = (bitField0_ & ~0x01000000);
         return this;
       }
 
@@ -4654,6 +4704,10 @@ public final class PTableProtos {
           to_bitField0_ |= 0x00080000;
         }
         result.storeNulls_ = storeNulls_;
+        if (((from_bitField0_ & 0x01000000) == 0x01000000)) {
+          to_bitField0_ |= 0x00100000;
+        }
+        result.baseColumnCount_ = baseColumnCount_;
         result.bitField0_ = to_bitField0_;
         onBuilt();
         return result;
@@ -4820,6 +4874,9 @@ public final class PTableProtos {
         if (other.hasStoreNulls()) {
           setStoreNulls(other.getStoreNulls());
         }
+        if (other.hasBaseColumnCount()) {
+          setBaseColumnCount(other.getBaseColumnCount());
+        }
         this.mergeUnknownFields(other.getUnknownFields());
         return this;
       }
@@ -6424,6 +6481,39 @@ public final class PTableProtos {
         return this;
       }
 
+      // optional int32 baseColumnCount = 25;
+      private int baseColumnCount_ ;
+      /**
+       * <code>optional int32 baseColumnCount = 25;</code>
+       */
+      public boolean hasBaseColumnCount() {
+        return ((bitField0_ & 0x01000000) == 0x01000000);
+      }
+      /**
+       * <code>optional int32 baseColumnCount = 25;</code>
+       */
+      public int getBaseColumnCount() {
+        return baseColumnCount_;
+      }
+      /**
+       * <code>optional int32 baseColumnCount = 25;</code>
+       */
+      public Builder setBaseColumnCount(int value) {
+        bitField0_ |= 0x01000000;
+        baseColumnCount_ = value;
+        onChanged();
+        return this;
+      }
+      /**
+       * <code>optional int32 baseColumnCount = 25;</code>
+       */
+      public Builder clearBaseColumnCount() {
+        bitField0_ = (bitField0_ & ~0x01000000);
+        baseColumnCount_ = 0;
+        onChanged();
+        return this;
+      }
+
       // @@protoc_insertion_point(builder_scope:PTable)
     }
 
@@ -6470,7 +6560,7 @@ public final class PTableProtos {
       "values\030\002 \003(\014\022\033\n\023guidePostsByteCount\030\003 
\001(" +
       "\003\022\025\n\rkeyBytesCount\030\004 
\001(\003\022\027\n\017guidePostsCo",
       "unt\030\005 \001(\005\022!\n\013pGuidePosts\030\006 
\001(\0132\014.PGuideP" +
-      "osts\"\266\004\n\006PTable\022\027\n\017schemaNameBytes\030\001 
\002(\014" +
+      "osts\"\317\004\n\006PTable\022\027\n\017schemaNameBytes\030\001 
\002(\014" +
       "\022\026\n\016tableNameBytes\030\002 
\002(\014\022\036\n\ttableType\030\003 " +
       "\002(\0162\013.PTableType\022\022\n\nindexState\030\004 
\001(\t\022\026\n\016" +
       "sequenceNumber\030\005 \002(\003\022\021\n\ttimeStamp\030\006 
\002(\003\022" +
@@ -6484,10 +6574,11 @@ public final class PTableProtos {
       "nt\030\022 \001(\014\022\025\n\rphysicalNames\030\023 
\003(\014\022\020\n\010tenan" +
       "tId\030\024 \001(\014\022\023\n\013viewIndexId\030\025 
\001(\005\022\021\n\tindexT" +
       "ype\030\026 \001(\014\022\026\n\016statsTimeStamp\030\027 
\001(\003\022\022\n\nsto" +
-      "reNulls\030\030 
\001(\010*A\n\nPTableType\022\n\n\006SYSTEM\020\000\022" +
-      
"\010\n\004USER\020\001\022\010\n\004VIEW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004"
 +
-      "B@\n(org.apache.phoenix.coprocessor.gener" +
-      "atedB\014PTableProtosH\001\210\001\001\240\001\001"
+      "reNulls\030\030 \001(\010\022\027\n\017baseColumnCount\030\031 
\001(\005*A" +
+      
"\n\nPTableType\022\n\n\006SYSTEM\020\000\022\010\n\004USER\020\001\022\010\n\004VI"
 +
+      
"EW\020\002\022\t\n\005INDEX\020\003\022\010\n\004JOIN\020\004B@\n(org.apache." 
+
+      "phoenix.coprocessor.generatedB\014PTablePro" +
+      "tosH\001\210\001\001\240\001\001"
     };
     com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner 
assigner =
       new 
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@@ -6511,7 +6602,7 @@ public final class PTableProtos {
           internal_static_PTable_fieldAccessorTable = new
             com.google.protobuf.GeneratedMessage.FieldAccessorTable(
               internal_static_PTable_descriptor,
-              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", });
+              new java.lang.String[] { "SchemaNameBytes", "TableNameBytes", 
"TableType", "IndexState", "SequenceNumber", "TimeStamp", "PkNameBytes", 
"BucketNum", "Columns", "Indexes", "IsImmutableRows", "GuidePosts", 
"DataTableNameBytes", "DefaultFamilyName", "DisableWAL", "MultiTenant", 
"ViewType", "ViewStatement", "PhysicalNames", "TenantId", "ViewIndexId", 
"IndexType", "StatsTimeStamp", "StoreNulls", "BaseColumnCount", });
           return null;
         }
       };

Reply via email to