Repository: phoenix
Updated Branches:
  refs/heads/4.x-HBase-0.98 4a7022066 -> 6e56eddc9


PHOENIX-2056 Ensure PK column from base table is added to any indexes on views


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

Branch: refs/heads/4.x-HBase-0.98
Commit: b95907ef64c3d93c8c263d5a9f5b1533e4256ee6
Parents: 4a70220
Author: Samarth <samarth.j...@salesforce.com>
Authored: Tue Jun 23 11:07:36 2015 -0700
Committer: Samarth <samarth.j...@salesforce.com>
Committed: Tue Jun 23 11:07:36 2015 -0700

----------------------------------------------------------------------
 .../apache/phoenix/end2end/AlterTableIT.java    | 170 ++++++++++++++++++-
 .../coprocessor/MetaDataEndpointImpl.java       | 145 +++++++++++++++-
 .../java/org/apache/phoenix/util/ByteUtil.java  |  10 +-
 3 files changed, 305 insertions(+), 20 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/b95907ef/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 61dd6a9..1302c60 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
@@ -2303,13 +2303,23 @@ public class AlterTableIT extends 
BaseOwnClusterHBaseManagedTimeIT {
 
             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));
+            
+            globalConn.createStatement().execute("SELECT * FROM " + baseTable);
+            
assertTrue(checkColumnPartOfPk(globalConn.unwrap(PhoenixConnection.class), 
"NEW_PK", baseTable));
+            
+            tenant1Conn.createStatement().execute("SELECT * FROM " + view1);
+            
assertTrue(checkColumnPartOfPk(tenant1Conn.unwrap(PhoenixConnection.class), 
"NEW_PK", view1));
+            
+            tenant1Conn.createStatement().execute("SELECT * FROM " + view2);
+            
assertTrue(checkColumnPartOfPk(tenant1Conn.unwrap(PhoenixConnection.class), 
"NEW_PK", view2));
+            
+            tenant2Conn.createStatement().execute("SELECT * FROM " + view3);
+            
assertTrue(checkColumnPartOfPk(tenant2Conn.unwrap(PhoenixConnection.class), 
"NEW_PK", view3));
+            
+            globalConn.createStatement().execute("SELECT * FROM " + view4);
+            
assertTrue(checkColumnPartOfPk(globalConn.unwrap(PhoenixConnection.class), 
"NEW_PK", view4));
 
         } finally {
             if (tenant1Conn != null) {
@@ -2344,4 +2354,152 @@ public class AlterTableIT extends 
BaseOwnClusterHBaseManagedTimeIT {
         tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
         return DriverManager.getConnection(getUrl(), tenantProps);
     }
+    
+    @Test
+    public void testAddPKColumnToBaseTableWhoseViewsHaveIndices() throws 
Exception {
+        String baseTable = "testAddPKColumnToBaseTableWhoseViewsHaveIndices";
+        String view1 = "view1";
+        String view2 = "view2";
+        String view3 = "view3";
+        String tenant1 = "tenant1";
+        String tenant2 = "tenant2";
+        String view2Index = view2 + "_idx";
+        String view3Index = view3 + "_idx";
+        /*                          baseTable(mutli-tenant)
+                                 /                           \                
+                         view1(tenant1)                  view3(tenant2, index) 
+                          /
+                        view2(tenant1, index)  
+         */
+        try (Connection globalConn = DriverManager.getConnection(getUrl())) {
+            // make sure that the tables are empty, but reachable
+            globalConn
+            .createStatement()
+            .execute(
+                    "CREATE TABLE "
+                            + baseTable
+                            + " (TENANT_ID VARCHAR NOT NULL, K1 varchar not 
null, V1 VARCHAR, V2 VARCHAR CONSTRAINT NAME_PK PRIMARY KEY(TENANT_ID, K1)) 
MULTI_TENANT = true ");
+
+        }
+        try (Connection tenantConn = getTenantConnection(tenant1)) {
+            // create tenant specific view for tenant1 - view1
+            tenantConn.createStatement().execute("CREATE VIEW " + view1 + " AS 
SELECT * FROM " + baseTable);
+            PhoenixConnection phxConn = 
tenantConn.unwrap(PhoenixConnection.class);
+            assertEquals(0, getTableSequenceNumber(phxConn, view1));
+            assertEquals(2, getMaxKeySequenceNumber(phxConn, view1));
+
+            // create a view - view2 on view - view1
+            tenantConn.createStatement().execute("CREATE VIEW " + view2 + " AS 
SELECT * FROM " + view1);
+            assertEquals(0, getTableSequenceNumber(phxConn, view2));
+            assertEquals(2, getMaxKeySequenceNumber(phxConn, view2));
+
+
+            // create an index on view2
+            tenantConn.createStatement().execute("CREATE INDEX " + view2Index 
+ " ON " + view2 + " (v1) include (v2)");
+            assertEquals(0, getTableSequenceNumber(phxConn, view2Index));
+            assertEquals(4, getMaxKeySequenceNumber(phxConn, view2Index));
+
+        }
+        try (Connection tenantConn = getTenantConnection(tenant2)) {
+            // create tenant specific view for tenant2 - view3
+            tenantConn.createStatement().execute("CREATE VIEW " + view3 + " AS 
SELECT * FROM " + baseTable);
+            PhoenixConnection phxConn = 
tenantConn.unwrap(PhoenixConnection.class);
+            assertEquals(0, getTableSequenceNumber(phxConn, view3));
+            assertEquals(2, getMaxKeySequenceNumber(phxConn, view3));
+            
+
+            // create an index on view3
+            tenantConn.createStatement().execute("CREATE INDEX " + view3Index 
+ " ON " + view3 + " (v1) include (v2)");
+            assertEquals(0, getTableSequenceNumber(phxConn, view3Index));
+            assertEquals(4, getMaxKeySequenceNumber(phxConn, view3Index));
+            
+
+        }
+
+        // alter the base table by adding 1 non-pk and 2 pk columns
+        try (Connection globalConn = DriverManager.getConnection(getUrl())) {
+            globalConn.createStatement().execute("ALTER TABLE " + baseTable + 
" ADD v3 VARCHAR, k2 VARCHAR PRIMARY KEY, k3 VARCHAR PRIMARY KEY");
+            assertEquals(4, 
getMaxKeySequenceNumber(globalConn.unwrap(PhoenixConnection.class), baseTable));
+            
+            // Upsert records in the base table
+            String upsert = "UPSERT INTO " + baseTable + " (TENANT_ID, K1, K2, 
K3, V1, V2, V3) VALUES (?, ?, ?, ?, ?, ?, ?)";
+            PreparedStatement stmt = globalConn.prepareStatement(upsert);
+            stmt.setString(1, tenant1);
+            stmt.setString(2, "K1");
+            stmt.setString(3, "K2");
+            stmt.setString(4, "K3");
+            stmt.setString(5, "V1");
+            stmt.setString(6, "V2");
+            stmt.setString(7, "V3");
+            stmt.executeUpdate();
+            stmt.setString(1, tenant2);
+            stmt.setString(2, "K11");
+            stmt.setString(3, "K22");
+            stmt.setString(4, "K33");
+            stmt.setString(5, "V11");
+            stmt.setString(6, "V22");
+            stmt.setString(7, "V33");
+            stmt.executeUpdate();
+            globalConn.commit();
+        }
+
+        // Verify now that the sequence number of data table, indexes and 
views have changed.
+        // Also verify that the newly added pk columns show up as pk columns 
of data table, indexes and views.
+        try (Connection tenantConn = getTenantConnection(tenant1)) {
+
+            ResultSet rs = tenantConn.createStatement().executeQuery("SELECT 
K2, K3, V3 FROM " + view1);
+            PhoenixConnection phxConn = 
tenantConn.unwrap(PhoenixConnection.class);
+            assertTrue(checkColumnPartOfPk(phxConn, "k2", view1));
+            assertTrue(checkColumnPartOfPk(phxConn, "k3", view1));
+            assertEquals(1, getTableSequenceNumber(phxConn, view1));
+            assertEquals(4, getMaxKeySequenceNumber(phxConn, view1));
+            verifyNewColumns(rs, "K2", "K3", "V3");
+            
+
+            rs = tenantConn.createStatement().executeQuery("SELECT K2, K3, V3 
FROM " + view2);
+            assertTrue(checkColumnPartOfPk(phxConn, "k2", view2));
+            assertTrue(checkColumnPartOfPk(phxConn, "k3", view2));
+            assertEquals(1, getTableSequenceNumber(phxConn, view2));
+            assertEquals(4, getMaxKeySequenceNumber(phxConn, view2));
+            verifyNewColumns(rs, "K2", "K3", "V3");
+            
+            assertTrue(checkColumnPartOfPk(phxConn, 
IndexUtil.getIndexColumnName(null, "k2"), view2Index));
+            assertTrue(checkColumnPartOfPk(phxConn, 
IndexUtil.getIndexColumnName(null, "k3"), view2Index));
+            assertEquals(1, getTableSequenceNumber(phxConn, view2Index));
+            assertEquals(6, getMaxKeySequenceNumber(phxConn, view2Index));
+        }
+        try (Connection tenantConn = getTenantConnection(tenant2)) {
+            ResultSet rs = tenantConn.createStatement().executeQuery("SELECT 
K2, K3, V3 FROM " + view3);
+            PhoenixConnection phxConn = 
tenantConn.unwrap(PhoenixConnection.class);
+            assertTrue(checkColumnPartOfPk(phxConn, "k2", view3));
+            assertTrue(checkColumnPartOfPk(phxConn, "k3", view3));
+            assertEquals(1, getTableSequenceNumber(phxConn, view3));
+            verifyNewColumns(rs, "K22", "K33", "V33");
+            
+            assertTrue(checkColumnPartOfPk(phxConn, 
IndexUtil.getIndexColumnName(null, "k2"), view3Index));
+            assertTrue(checkColumnPartOfPk(phxConn, 
IndexUtil.getIndexColumnName(null, "k3"), view3Index));
+            assertEquals(1, getTableSequenceNumber(phxConn, view3Index));
+            assertEquals(6, getMaxKeySequenceNumber(phxConn, view3Index));
+        }
+    }
+    
+    private static long getTableSequenceNumber(PhoenixConnection conn, String 
tableName) throws SQLException {
+        PTable table = conn.getMetaDataCache().getTable(new 
PTableKey(conn.getTenantId(), SchemaUtil.normalizeIdentifier(tableName)));
+        return table.getSequenceNumber();
+    }
+    
+    private static short getMaxKeySequenceNumber(PhoenixConnection conn, 
String tableName) throws SQLException {
+        PTable table = conn.getMetaDataCache().getTable(new 
PTableKey(conn.getTenantId(), SchemaUtil.normalizeIdentifier(tableName)));
+        return SchemaUtil.getMaxKeySeq(table);
+    }
+    
+    private static void verifyNewColumns(ResultSet rs, String ... values) 
throws SQLException {
+        assertTrue(rs.next());
+        int i = 1;
+        for (String value : values) {
+            assertEquals(value, rs.getString(i++));
+        }
+        assertFalse(rs.next());
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b95907ef/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 f80a69d..af0c7de 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
@@ -62,7 +62,9 @@ import static 
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID_BYTE
 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.query.QueryConstants.SEPARATOR_BYTE_ARRAY;
 import static org.apache.phoenix.schema.PTableType.INDEX;
+import static org.apache.phoenix.util.ByteUtil.EMPTY_BYTE_ARRAY;
 import static org.apache.phoenix.util.SchemaUtil.getVarCharLength;
 import static org.apache.phoenix.util.SchemaUtil.getVarChars;
 
@@ -166,11 +168,13 @@ import org.apache.phoenix.schema.types.PBoolean;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.schema.types.PInteger;
 import org.apache.phoenix.schema.types.PLong;
+import org.apache.phoenix.schema.types.PSmallint;
 import org.apache.phoenix.schema.types.PVarbinary;
 import org.apache.phoenix.schema.types.PVarchar;
 import org.apache.phoenix.trace.util.Tracing;
 import org.apache.phoenix.util.ByteUtil;
 import org.apache.phoenix.util.EnvironmentEdgeManager;
+import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.KeyValueUtil;
 import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.QueryUtil;
@@ -1580,13 +1584,13 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             // lock the rows corresponding to views so that no other thread 
can modify the view meta-data
             RowLock viewRowLock = acquireLock(region, viewKey, locks);
             PTable view = doGetTable(viewKey, clientTimeStamp, viewRowLock);
-
             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;
             }
             int deltaNumberOfColumns = 0;
+            short deltaNumPkColsSoFar = 0;
             for (Mutation m : tableMetadata) {
                 byte[][] rkmd = new byte[5][];
                 int pkCount = getVarChars(m.getRow(), rkmd);
@@ -1595,16 +1599,133 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                         && 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);
+                    byte[] columnKey = ByteUtil.concat(viewKey, 
QueryConstants.SEPARATOR_BYTE_ARRAY, rkmd[COLUMN_NAME_INDEX]);
+                    if (rkmd[FAMILY_NAME_INDEX] != null) {
+                        columnKey = ByteUtil.concat(columnKey, 
QueryConstants.SEPARATOR_BYTE_ARRAY, rkmd[FAMILY_NAME_INDEX]);
+                    }
+                    Put viewColumnDefinitionPut = new Put(columnKey, 
clientTimeStamp);
                     for (Cell cell : 
p.getFamilyCellMap().values().iterator().next()) {
-                        viewColumnDefinitionPut.add(CellUtil.createCell(k, 
CellUtil.cloneFamily(cell),
+                        
viewColumnDefinitionPut.add(CellUtil.createCell(columnKey, 
CellUtil.cloneFamily(cell),
                                 CellUtil.cloneQualifier(cell), 
cell.getTimestamp(), cell.getTypeByte(),
                                 CellUtil.cloneValue(cell)));
                     }
                     deltaNumberOfColumns++;
                     
mutationsForAddingColumnsToViews.add(viewColumnDefinitionPut);
+                    if (rkmd[FAMILY_NAME_INDEX] == null && 
rkmd[COLUMN_NAME_INDEX] != null) {
+                        /*
+                         * If adding a pk column to the base table (and hence 
the view), see if there are any indexes on
+                         * the view. If yes, then generate puts for the index 
header row and column rows.
+                         */
+                        deltaNumPkColsSoFar++;
+                        for (PTable index : view.getIndexes()) {
+                            int oldNumberOfColsInIndex = 
index.getColumns().size();
+                            
+                            byte[] indexColumnKey = 
ByteUtil.concat(getViewIndexHeaderRowKey(index),
+                                    QueryConstants.SEPARATOR_BYTE_ARRAY,
+                                    
IndexUtil.getIndexColumnName(rkmd[FAMILY_NAME_INDEX], rkmd[COLUMN_NAME_INDEX]));
+                            Put indexColumnDefinitionPut = new 
Put(indexColumnKey, clientTimeStamp);
+                            
+                            // Set the index specific data type for the column
+                            List<Cell> dataTypes = viewColumnDefinitionPut
+                                    
.get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                            
PhoenixDatabaseMetaData.DATA_TYPE_BYTES);
+                            if (dataTypes != null && dataTypes.size() > 0) {
+                                Cell dataType = dataTypes.get(0);
+                                int dataColumnDataType = 
PInteger.INSTANCE.getCodec().decodeInt(
+                                        dataType.getValueArray(), 
dataType.getValueOffset(), SortOrder.ASC);
+                                int indexColumnDataType = 
IndexUtil.getIndexColumnDataType(true,
+                                        
PDataType.fromTypeId(dataColumnDataType)).getSqlType();
+                                byte[] indexColumnDataTypeBytes = new 
byte[PInteger.INSTANCE.getByteSize()];
+                                
PInteger.INSTANCE.getCodec().encodeInt(indexColumnDataType, 
indexColumnDataTypeBytes, 0);
+                                
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.DATA_TYPE_BYTES, indexColumnDataTypeBytes);
+                            }
+                            
+                            // Set precision
+                            List<Cell> decimalDigits = 
viewColumnDefinitionPut.get(
+                                    PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    
PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES);
+                            if (decimalDigits != null && decimalDigits.size() 
> 0) {
+                                Cell decimalDigit = decimalDigits.get(0);
+                                
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.DECIMAL_DIGITS_BYTES, decimalDigit.getValueArray());
+                            }
+                            
+                            // Set size
+                            List<Cell> columnSizes = 
viewColumnDefinitionPut.get(
+                                    PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES);
+                            if (columnSizes != null && columnSizes.size() > 0) 
{
+                                Cell columnSize = columnSizes.get(0);
+                                
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.COLUMN_SIZE_BYTES, columnSize.getValueArray());
+                            }
+                            
+                            // Set sort order
+                            List<Cell> sortOrders = 
viewColumnDefinitionPut.get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.SORT_ORDER_BYTES);
+                            if (sortOrders != null && sortOrders.size() > 0) {
+                                Cell sortOrder = sortOrders.get(0);
+                                
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.SORT_ORDER_BYTES, sortOrder.getValueArray());
+                            }
+                            
+                            // Set data table name
+                            List<Cell> dataTableNames = 
viewColumnDefinitionPut.get(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    
PhoenixDatabaseMetaData.DATA_TABLE_NAME_BYTES);
+                            if (dataTableNames != null && 
dataTableNames.size() > 0) {
+                                Cell dataTableName = dataTableNames.get(0);
+                                
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.DATA_TABLE_NAME_BYTES, dataTableName.getValueArray());
+                            }
+                            
+                            // Set the ordinal position of the new column.
+                            byte[] ordinalPositionBytes = new 
byte[PInteger.INSTANCE.getByteSize()];
+                            int ordinalPositionOfNewCol = 
oldNumberOfColsInIndex + deltaNumPkColsSoFar;
+                            
PInteger.INSTANCE.getCodec().encodeInt(ordinalPositionOfNewCol, 
ordinalPositionBytes, 0);
+                            
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.ORDINAL_POSITION_BYTES, ordinalPositionBytes);
+                            
+                            // New PK columns have to be nullable after the 
first DDL
+                            byte[] isNullableBytes = 
PBoolean.INSTANCE.toBytes(true);
+                            
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                        
PhoenixDatabaseMetaData.NULLABLE_BYTES, isNullableBytes);
+                            
+                            // Set the key sequence for the pk column to be 
added
+                            short currentKeySeq = 
SchemaUtil.getMaxKeySeq(index);
+                            short newKeySeq = (short)(currentKeySeq + 
deltaNumPkColsSoFar);
+                            byte[] keySeqBytes = new 
byte[PSmallint.INSTANCE.getByteSize()];
+                            
PSmallint.INSTANCE.getCodec().encodeShort(newKeySeq, keySeqBytes, 0);
+                            
indexColumnDefinitionPut.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                                    PhoenixDatabaseMetaData.KEY_SEQ_BYTES, 
keySeqBytes);
+                            
+                            
mutationsForAddingColumnsToViews.add(indexColumnDefinitionPut);
+                        }
+                    }
+                }
+            }
+            if (deltaNumPkColsSoFar > 0) {
+                for (PTable index : view.getIndexes()) {
+                    byte[] indexHeaderRowKey = getViewIndexHeaderRowKey(index);
+                    Put indexHeaderRowMutation = new Put(indexHeaderRowKey);
+                    
+                    // increment sequence number
+                    long newSequenceNumber = index.getSequenceNumber() + 1;
+                    byte[] newSequenceNumberPtr = new 
byte[PLong.INSTANCE.getByteSize()];
+                    PLong.INSTANCE.getCodec().encodeLong(newSequenceNumber, 
newSequenceNumberPtr, 0);
+                    
indexHeaderRowMutation.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                            PhoenixDatabaseMetaData.TABLE_SEQ_NUM_BYTES, 
newSequenceNumberPtr);
+                    
+                    // increase the column count
+                    int newColumnCount = index.getColumns().size() + 
deltaNumPkColsSoFar;
+                    byte[] newColumnCountPtr = new 
byte[PInteger.INSTANCE.getByteSize()];
+                    PInteger.INSTANCE.getCodec().encodeInt(newColumnCount, 
newColumnCountPtr, 0);
+                    
indexHeaderRowMutation.add(PhoenixDatabaseMetaData.TABLE_FAMILY_BYTES,
+                            PhoenixDatabaseMetaData.COLUMN_COUNT_BYTES, 
newColumnCountPtr);
+                    
+                    // add index row header key to the invalidate list to 
force clients to fetch the latest meta-data
+                    invalidateList.add(new 
ImmutableBytesPtr(indexHeaderRowKey));
+                    
mutationsForAddingColumnsToViews.add(indexHeaderRowMutation);
                 }
             }
 
@@ -1631,7 +1752,10 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
                     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);
+                            .getBytes());
+                    if (column.getFamilyName() != null) {
+                        k = ByteUtil.concat(k, 
QueryConstants.SEPARATOR_BYTE_ARRAY, column.getFamilyName().getBytes());
+                    }
 
                     Put positionUpdatePut = new Put(k, clientTimeStamp);
                     byte[] ptr = new byte[PInteger.INSTANCE.getByteSize()];
@@ -1644,7 +1768,14 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             invalidateList.add(new ImmutableBytesPtr(viewKey));
         }
     }
-
+    
+    private byte[] getViewIndexHeaderRowKey(PTable index) {
+        byte[] tenantIdBytes = index.getKey().getTenantId() != null ? 
index.getKey().getTenantId().getBytes() : EMPTY_BYTE_ARRAY;
+        byte[] schemaNameBytes = index.getSchemaName() != null ? 
index.getSchemaName().getBytes() : EMPTY_BYTE_ARRAY; 
+        byte[] tableNameBytes = index.getTableName().getBytes();
+        return ByteUtil.concat(tenantIdBytes, SEPARATOR_BYTE_ARRAY, 
schemaNameBytes, SEPARATOR_BYTE_ARRAY, tableNameBytes);
+    }
+    
     @Override
     public void addColumn(RpcController controller, AddColumnRequest request,
             RpcCallback<MetaDataResponse> done) {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/b95907ef/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java 
b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
index 1f4a285..1e3516d 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ByteUtil.java
@@ -253,17 +253,13 @@ public class ByteUtil {
     public static byte[] concat(byte[] first, byte[]... rest) {
         int totalLength = first.length;
         for (byte[] array : rest) {
-            if (array != null) {
-                totalLength += array.length;
-            }
+            totalLength += array.length;
         }
         byte[] result = Arrays.copyOf(first, totalLength);
         int offset = first.length;
         for (byte[] array : rest) {
-            if (array != null) {
-                System.arraycopy(array, 0, result, offset, array.length);
-                offset += array.length;
-            }
+            System.arraycopy(array, 0, result, offset, array.length);
+            offset += array.length;
         }
         return result;
     }

Reply via email to