PHOENIX-1367 VIEW derived from another VIEW doesn't use parent VIEW indexes


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

Branch: refs/heads/calcite
Commit: 5a6496e1dc51576f4882c3718495eb2e1d6bce0a
Parents: 8e082fb
Author: Cody Marcel <cmar...@salesforce.com>
Authored: Mon Jul 18 17:58:32 2016 -0600
Committer: Thomas D'Silva <tdsi...@salesforce.com>
Committed: Sun Sep 11 11:33:53 2016 -0700

----------------------------------------------------------------------
 .../index/ChildViewsUseParentViewIndexIT.java   | 254 +++++++++++++++++++
 .../coprocessor/MetaDataEndpointImpl.java       |  14 +-
 .../apache/phoenix/index/IndexMaintainer.java   |  16 +-
 .../apache/phoenix/query/QueryConstants.java    |   1 +
 .../apache/phoenix/schema/MetaDataClient.java   |  67 +++--
 .../apache/phoenix/schema/PMetaDataImpl.java    |   2 +-
 .../java/org/apache/phoenix/schema/PTable.java  |  20 +-
 .../org/apache/phoenix/schema/PTableImpl.java   |  41 ++-
 .../org/apache/phoenix/util/ExpressionUtil.java |  15 +-
 9 files changed, 360 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java
 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java
new file mode 100644
index 0000000..60a428c
--- /dev/null
+++ 
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/ChildViewsUseParentViewIndexIT.java
@@ -0,0 +1,254 @@
+package org.apache.phoenix.end2end.index;
+
+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.Date;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import org.apache.phoenix.end2end.BaseHBaseManagedTimeTableReuseIT;
+import org.apache.phoenix.util.QueryUtil;
+import org.junit.Test;
+
+public class ChildViewsUseParentViewIndexIT extends 
BaseHBaseManagedTimeTableReuseIT {
+
+    @Test
+    public void testIndexOnParentViewWithTenantSpecificConnection() throws 
Exception {
+        final String baseTableName = generateRandomString();
+        final String globalViewName = generateRandomString();
+        final String globalViewIdxName = generateRandomString();
+        final String tenantViewName1 = generateRandomString();
+        final String tenantViewName2 = generateRandomString();
+
+        // Set up props with TenantId
+        Properties props  = new Properties();
+        props.setProperty("TenantId", "00Dxxxxxxxxxxx1");
+        
+        try (Connection conn = DriverManager.getConnection(getUrl());
+                Connection tenantConn = DriverManager.getConnection(getUrl(), 
props)) {
+            // Create Base table
+            createBaseTable(baseTableName, conn);
+            
+            // Create the Global View on the Base Table for a value of KP
+            createGlobalView(globalViewName, baseTableName, conn);
+    
+            // Create Global Index on View
+            createGlobalIndexOnView(globalViewName, globalViewIdxName, conn);
+    
+            // Create tenant specific view which is a child of global view
+            createTenantSpecificView(globalViewName, tenantViewName1, 
tenantConn);
+            
+            // Create tenant specific view which is a child of previous tenant 
view
+            createTenantSpecificView(tenantViewName1, tenantViewName2, 
tenantConn);
+    
+            int rowCount = 0;
+            insertRowIntoView(globalViewName, tenantConn, ++rowCount);
+            insertRowIntoView(globalViewName, tenantConn, ++rowCount);
+            insertRowIntoView(globalViewName, tenantConn, ++rowCount);
+            assertQueryIndex(globalViewName, baseTableName, tenantConn, 3);
+    
+            insertRowIntoView(tenantViewName1, tenantConn, ++rowCount);
+            insertRowIntoView(tenantViewName2, tenantConn, ++rowCount);
+    
+            // assert that we get 5 rows while querying via tenant specific 
views and that we use the index
+            assertQueryIndex(tenantViewName1, baseTableName, tenantConn, 5);
+            assertQueryIndex(tenantViewName2, baseTableName, tenantConn, 5);
+    
+            // assert that we get 5 rows while querying via global specific 
view and that we use the index
+            assertQueryIndex(globalViewName, baseTableName, tenantConn, 5);
+        }
+    }
+    
+    
+    @Test
+    public void testParentViewIndexWithSpecializedChildViews() throws 
Exception {
+        final String baseTableName = generateRandomString();
+        final String parentViewName = generateRandomString();
+        final String parentViewIdxName = generateRandomString();
+        final String childViewName1 = generateRandomString();
+        final String childViewName2 = generateRandomString();
+
+        try (Connection conn = DriverManager.getConnection(getUrl())) {
+            // create base table
+            String baseTableDdl = "CREATE TABLE " + baseTableName + " (" +
+                    "A0 CHAR(1) NOT NULL PRIMARY KEY," +
+                    "A1 CHAR(1)," +
+                    "A2 CHAR(1)," +
+                    "A3 CHAR(1)," +
+                    "A4 CHAR(1))";
+            conn.createStatement().execute(baseTableDdl);
+            
+            // create the parent view on the base table for a value of A
+            String globalViewDdl = "CREATE VIEW " + parentViewName + " AS 
SELECT * FROM " + baseTableName + " WHERE A1 = 'X'";
+            conn.createStatement().execute(globalViewDdl);
+    
+            // create index on parent view
+            conn.createStatement().execute("CREATE INDEX " + parentViewIdxName 
+ " ON " + parentViewName + "(A4, A2)");
+    
+            // create child of parent view that should be able to use the 
parent's index
+            String childViewDdl = "CREATE VIEW " + childViewName1 + " AS 
SELECT * FROM " + parentViewName + " WHERE A2 = 'Y'";
+            conn.createStatement().execute(childViewDdl);
+            
+            // create child of parent view that should *not* be able to use 
the parent's index
+            String grandChildViewDdl1 = "CREATE VIEW " + childViewName2 + " AS 
SELECT * FROM " + childViewName1 + " WHERE A3 = 'Z'";
+            conn.createStatement().execute(grandChildViewDdl1);
+
+            // upsert row using parent view
+            PreparedStatement stmt = conn.prepareStatement("UPSERT INTO " + 
parentViewName + " (A0, A2, A3, A4) VALUES(?,?,?,?)");
+            stmt.setString(1, "1");
+            stmt.setString(2, "Y");
+            stmt.setString(3, "Z");
+            stmt.setString(4, "1");
+            stmt.execute();
+            conn.commit();
+            
+            // upsert row using first child view
+            stmt = conn.prepareStatement("UPSERT INTO " + childViewName1 + " 
(A0, A3, A4) VALUES(?,?,?)");
+            stmt.setString(1, "2");
+            stmt.setString(2, "Z");
+            stmt.setString(3, "2");
+            stmt.execute();
+            conn.commit();
+            
+            // upsert row using second child view
+            stmt = conn.prepareStatement("UPSERT INTO " + childViewName2 + " 
(A0, A4) VALUES(?,?)");
+            stmt.setString(1, "3");
+            stmt.setString(2, "3");
+            stmt.execute();
+            conn.commit();
+            
+            // assert that we get 2 rows while querying via parent views and 
that we use the index
+            assertQueryUsesIndex(baseTableName, parentViewName, conn, false);
+            
+            // assert that we get 2 rows while querying via the first child 
view and that we use the index
+            assertQueryUsesIndex(baseTableName, childViewName1, conn, true);
+            
+            // assert that we get 3 rows while querying via the second child 
view and that we use the base table
+            assertQueryUsesBaseTable(baseTableName, childViewName2, conn);
+        }
+    }
+
+    private void assertQueryUsesIndex(final String baseTableName, final String 
viewName, Connection conn, boolean isChildView) throws SQLException {
+        String sql = "SELECT A0, A1, A2, A4 FROM " + viewName +" WHERE A4 IN 
('1', '2', '3') ORDER BY A4, A2";
+        ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery();
+        String childViewScanKey = isChildView ? ",'Y'" : "";
+        assertEquals(
+            "CLIENT PARALLEL 1-WAY SKIP SCAN ON 3 KEYS OVER _IDX_" + 
baseTableName + " [-32768,'1'" + childViewScanKey + "] - [-32768,'3'" + 
childViewScanKey + "]\n" +
+            "    SERVER FILTER BY FIRST KEY ONLY",
+            QueryUtil.getExplainPlan(rs));
+        
+        rs = conn.createStatement().executeQuery(sql);
+        assertTrue(rs.next());
+        assertEquals("1", rs.getString(1));
+        assertEquals("X", rs.getString(2));
+        assertEquals("Y", rs.getString(3));
+        assertEquals("1", rs.getString(4));
+        assertTrue(rs.next());
+        assertEquals("2", rs.getString(1));
+        assertEquals("X", rs.getString(2));
+        assertEquals("Y", rs.getString(3));
+        assertEquals("2", rs.getString(4));
+        assertFalse(rs.next());
+    }
+    
+    private void assertQueryUsesBaseTable(final String baseTableName, final 
String viewName, Connection conn) throws SQLException {
+        String sql = "SELECT A0, A1, A2, A4 FROM " + viewName +" WHERE A4 IN 
('1', '2', '3') ";
+        ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery();
+        assertEquals(
+            "CLIENT PARALLEL 1-WAY FULL SCAN OVER " + baseTableName + "\n" +
+            "    SERVER FILTER BY (A4 IN ('1','2','3') AND ((A1 = 'X' AND A2 = 
'Y') AND A3 = 'Z'))",
+            QueryUtil.getExplainPlan(rs));
+        
+        rs = conn.createStatement().executeQuery(sql);
+        assertTrue(rs.next());
+        assertEquals("1", rs.getString(1));
+        assertEquals("X", rs.getString(2));
+        assertEquals("Y", rs.getString(3));
+        assertEquals("1", rs.getString(4));
+        assertTrue(rs.next());
+        assertEquals("2", rs.getString(1));
+        assertEquals("X", rs.getString(2));
+        assertEquals("Y", rs.getString(3));
+        assertEquals("2", rs.getString(4));
+        assertTrue(rs.next());
+        assertEquals("3", rs.getString(1));
+        assertEquals("X", rs.getString(2));
+        assertEquals("Y", rs.getString(3));
+        assertEquals("3", rs.getString(4));
+        assertFalse(rs.next());
+    }
+
+    private void createBaseTable(String baseTableName, Connection conn) throws 
SQLException {
+        String baseTableDdl = "CREATE TABLE " + baseTableName + " (" +
+                "OID CHAR(15) NOT NULL,\n" +
+                "KP CHAR(3) NOT NULL\n," +
+                "CREATED_DATE DATE\n," +
+                "CREATED_BY CHAR(3)\n," +
+                "CONSTRAINT PK PRIMARY KEY (oid, kp))\n";
+        String ddlOptions = "MULTI_TENANT=true, IMMUTABLE_ROWS=TRUE, 
VERSIONS=1";
+        conn.createStatement().execute(baseTableDdl + ddlOptions);
+    }
+
+    private void createGlobalView(String globalViewName, String baseTableName, 
Connection conn) throws SQLException {
+        String globalViewDdl = "CREATE VIEW IF NOT EXISTS " + globalViewName + 
" (" +
+                "A_DATE DATE NOT NULL," +
+                "WO_ID CHAR(15) NOT NULL," +
+                "WA_ID CHAR(15) NOT NULL," +
+                "C_TYPE VARCHAR NOT NULL," +
+                "CA_TYPE VARCHAR NOT NULL," +
+                "V_ID CHAR(15)," +
+                "C_CTX VARCHAR," +
+                "CONSTRAINT PKVIEW PRIMARY KEY (A_DATE, WO_ID, WA_ID, C_TYPE, 
CA_TYPE))" +
+                "AS SELECT * FROM " + baseTableName + " WHERE KP = 'xyz'";
+        conn.createStatement().execute(globalViewDdl);
+    }
+
+    private void createGlobalIndexOnView(String globalViewName, String 
globalViewIdxName, Connection conn) throws SQLException {
+        String globalViewIdxDdl = "CREATE INDEX IF NOT EXISTS " + 
globalViewIdxName +
+                " ON " + globalViewName + "(WO_ID, A_DATE DESC)";
+        conn.createStatement().execute(globalViewIdxDdl);
+    }
+
+    private void createTenantSpecificView(String parentViewName, String 
tenantViewName, Connection conn) throws SQLException {
+        String tenantSpecificViewDdl = "CREATE VIEW IF NOT EXISTS " +
+                tenantViewName + " AS SELECT * FROM " + parentViewName;
+        conn.createStatement().
+                execute(tenantSpecificViewDdl);
+    }
+    
+    private void insertRowIntoView(String viewName, Connection conn, int 
rowCount) throws SQLException {
+        PreparedStatement stmt = conn.prepareStatement(
+                "UPSERT INTO " + viewName + " (A_DATE, WO_ID, WA_ID, C_TYPE, 
CA_TYPE, V_ID) VALUES(?,?,?,?,?,?)");
+        stmt.setDate(1, new Date(System.currentTimeMillis()));
+        stmt.setString(2, "003xxxxxxxxxxx" + rowCount);
+        stmt.setString(3, "701xxxxxxxxxxx" + rowCount);
+        stmt.setString(4, "ctype1");
+        stmt.setString(5, "catype1");
+        stmt.setString(6, "xyzxxxxxxxxxxx" + rowCount);
+        stmt.execute();
+        conn.commit();
+    }
+
+    private void assertQueryIndex(String viewName, String baseTableName, 
Connection conn, int expectedRows) throws SQLException {
+        String sql = "SELECT WO_ID FROM " + viewName +
+                " WHERE WO_ID IN ('003xxxxxxxxxxx1', '003xxxxxxxxxxx2', 
'003xxxxxxxxxxx3', '003xxxxxxxxxxx4', '003xxxxxxxxxxx5') " +
+                " AND (A_DATE > TO_DATE('2016-01-01 06:00:00.0')) " +
+                " ORDER BY WO_ID, A_DATE DESC";
+        ResultSet rs = conn.prepareStatement("EXPLAIN " + sql).executeQuery();
+        assertEquals(
+            "CLIENT PARALLEL 1-WAY SKIP SCAN ON 5 RANGES OVER _IDX_" + 
baseTableName + " [-32768,'00Dxxxxxxxxxxx1','003xxxxxxxxxxx1',*] - 
[-32768,'00Dxxxxxxxxxxx1','003xxxxxxxxxxx5',~'2016-01-01 06:00:00.000']\n" + 
+            "    SERVER FILTER BY FIRST KEY ONLY",
+            QueryUtil.getExplainPlan(rs));
+        
+        rs = conn.createStatement().executeQuery(sql);
+        for (int i=0; i<expectedRows; ++i)
+            assertTrue(rs.next());
+        assertFalse(rs.next());
+    }
+}

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/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 7d3468d..a5d77bc 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
@@ -924,8 +924,10 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         
         
         List<PColumn> columns = 
Lists.newArrayListWithExpectedSize(columnCount);
-        List<PTable> indexes = new ArrayList<PTable>();
-        List<PName> physicalTables = new ArrayList<PName>();
+        List<PTable> indexes = Lists.newArrayList();
+        List<PName> physicalTables = Lists.newArrayList();
+        PName parentTableName = tableType == INDEX ? dataTableName : null;
+        PName parentSchemaName = tableType == INDEX ? schemaName : null;
         while (true) {
           results.clear();
           scanner.next(results);
@@ -943,6 +945,9 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
                   addIndexToTable(tenantId, schemaName, famName, tableName, 
clientTimeStamp, indexes);
               } else if (linkType == LinkType.PHYSICAL_TABLE) {
                   physicalTables.add(famName);
+              } else if (linkType == LinkType.PARENT_TABLE) {
+                  parentTableName = 
PNameFactory.newName(SchemaUtil.getTableNameFromFullName(famName.getBytes()));
+                  parentSchemaName = 
PNameFactory.newName(SchemaUtil.getSchemaNameFromFullName(famName.getBytes()));
               }
           } else {
               addColumnToTable(results, colName, famName, colKeyValues, 
columns, saltBucketNum != null);
@@ -951,8 +956,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol 
implements Coprocesso
         // Avoid querying the stats table because we're holding the rowLock 
here. Issuing an RPC to a remote
         // server while holding this lock is a bad idea and likely to cause 
contention.
         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,
+                pkName, saltBucketNum, columns, parentSchemaName, 
parentTableName, indexes, isImmutableRows, physicalTables, defaultFamilyName,
                 viewStatement, disableWAL, multiTenant, storeNulls, viewType, 
viewIndexId, indexType,
                 rowKeyOrderOptimizable, transactional, updateCacheFrequency, 
baseColumnCount,
                 indexDisableTimestamp, isNamespaceMapped, autoPartitionSeq, 
isAppendOnlySchema);
@@ -1718,7 +1722,7 @@ public class MetaDataEndpointImpl extends 
MetaDataProtocol implements Coprocesso
             return new MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND, 
EnvironmentEdgeManager.currentTimeMillis(), null);
         }
         // Make sure we're not deleting the "wrong" child
-        if (!Arrays.equals(parentTableName, table.getParentTableName() == null 
? null : table.getParentTableName().getBytes())) {
+        if (parentTableName!=null && table.getParentTableName() != null && 
!Arrays.equals(parentTableName, table.getParentTableName().getBytes())) {
             return new MetaDataMutationResult(MutationCode.TABLE_NOT_FOUND, 
EnvironmentEdgeManager.currentTimeMillis(), null);
         }
         // Since we don't allow back in time DDL, we know if we have a table 
it's the one

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java 
b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
index ee67ca2..6595562 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/index/IndexMaintainer.java
@@ -50,6 +50,7 @@ import org.apache.phoenix.compile.ColumnResolver;
 import org.apache.phoenix.compile.FromCompiler;
 import org.apache.phoenix.compile.IndexExpressionCompiler;
 import org.apache.phoenix.compile.StatementContext;
+import org.apache.phoenix.expression.CoerceExpression;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.ExpressionType;
 import org.apache.phoenix.expression.KeyValueColumnExpression;
@@ -85,10 +86,12 @@ import org.apache.phoenix.schema.tuple.ValueGetterTuple;
 import org.apache.phoenix.schema.types.PDataType;
 import org.apache.phoenix.util.BitSet;
 import org.apache.phoenix.util.ByteUtil;
+import org.apache.phoenix.util.ExpressionUtil;
 import org.apache.phoenix.util.IndexUtil;
 import org.apache.phoenix.util.MetaDataUtil;
 import org.apache.phoenix.util.SchemaUtil;
 import org.apache.phoenix.util.TrustedByteArrayOutputStream;
+import org.apache.tephra.TxConstants;
 
 import com.google.common.base.Predicate;
 import com.google.common.collect.Iterators;
@@ -96,8 +99,6 @@ import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
 import com.google.common.collect.Sets;
 
-import org.apache.tephra.TxConstants;
-
 /**
  * 
  * Class that builds index row key from data row key and current state of
@@ -416,7 +417,16 @@ public class IndexMaintainer implements Writable, 
Iterable<ColumnReference> {
                        this.rowKeyMetaData.setIndexPkPosition(dataPkPos, 
indexPos);
                    } else {
                        indexColByteSize += column.getDataType().isFixedWidth() 
? SchemaUtil.getFixedByteSize(column) : 
ValueSchema.ESTIMATED_VARIABLE_LENGTH_SIZE;
-                       this.indexedExpressions.add(expression);
+                       try {
+                           // Surround constant with cast so that we can still 
know the original type. Otherwise, if we lose the type,
+                           // (for example when VARCHAR becomes CHAR), it can 
lead to problems in the type translation we do between data tables and indexes.
+                           if (column.isNullable() && 
ExpressionUtil.isConstant(expression)) {
+                               expression = 
CoerceExpression.create(expression, indexColumn.getDataType());
+                           }
+                        this.indexedExpressions.add(expression);
+                    } catch (SQLException e) {
+                        throw new RuntimeException(e); // Impossible
+                    }
                    }
             }
             else {

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java 
b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
index 7e5f7a2..155d1ba 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryConstants.java
@@ -131,6 +131,7 @@ import org.apache.phoenix.util.ByteUtil;
 public interface QueryConstants {
     public static final String NAME_SEPARATOR = ".";
     public static final String NAMESPACE_SEPARATOR = ":";
+    public static final String CHILD_VIEW_INDEX_NAME_SEPARATOR = "#";
     public static final byte[] NAMESPACE_SEPARATOR_BYTES = 
Bytes.toBytes(NAMESPACE_SEPARATOR);
     public static final byte NAMESPACE_SEPARATOR_BYTE = 
NAMESPACE_SEPARATOR_BYTES[0];
     public static final String NAME_SEPARATOR_REGEX = "\\" + NAME_SEPARATOR;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index fd6b48c..7da7010 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -541,7 +541,7 @@ public class MetaDataClient {
                         // In this case, we update the parent table which may 
in turn pull
                         // in indexes to add to this table.
                         long resolvedTime = 
TransactionUtil.getResolvedTime(connection, result);
-                        if (addIndexesFromPhysicalTable(result, 
resolvedTimestamp)) {
+                        if (addIndexesFromParentTable(result, 
resolvedTimestamp)) {
                             connection.addTable(result.getTable(), 
resolvedTime);
                         }
                         else {
@@ -656,7 +656,7 @@ public class MetaDataClient {
     }
 
     /**
-     * Fault in the physical table to the cache and add any indexes it has to 
the indexes
+     * Fault in the parent table to the cache and add any indexes it has to 
the indexes
      * of the table for which we just updated.
      * TODO: combine this round trip with the one that updates the cache for 
the child table.
      * @param result the result from updating the cache for the current table.
@@ -664,29 +664,30 @@ public class MetaDataClient {
      * @return true if the PTable contained by result was modified and false 
otherwise
      * @throws SQLException if the physical table cannot be found
      */
-    private boolean addIndexesFromPhysicalTable(MetaDataMutationResult result, 
Long resolvedTimestamp) throws SQLException {
+    private boolean addIndexesFromParentTable(MetaDataMutationResult result, 
Long resolvedTimestamp) throws SQLException {
         PTable view = result.getTable();
         // If not a view or if a view directly over an HBase table, there's 
nothing to do
         if (view.getType() != PTableType.VIEW || view.getViewType() == 
ViewType.MAPPED) {
             return false;
         }
-        String physicalName = view.getPhysicalName().getString();
-        String schemaName = SchemaUtil.getSchemaNameFromFullName(physicalName);
-        String tableName = SchemaUtil.getTableNameFromFullName(physicalName);
-        MetaDataMutationResult parentResult = updateCache(null, schemaName, 
tableName, false, resolvedTimestamp);
-        PTable physicalTable = parentResult.getTable();
-        if (physicalTable == null) {
+        // a view on a table will not have a parent name but will have a 
physical table name (which is the parent)
+        String parentName = view.getParentName().getString();
+        String schemaName = SchemaUtil.getSchemaNameFromFullName(parentName);
+        String tableName = SchemaUtil.getTableNameFromFullName(parentName);
+        MetaDataMutationResult parentResult = 
updateCache(connection.getTenantId(), schemaName, tableName, false, 
resolvedTimestamp);
+        PTable parentTable = parentResult.getTable();
+        if (parentTable == null) {
             throw new TableNotFoundException(schemaName, tableName);
         }
         if (!result.wasUpdated() && !parentResult.wasUpdated()) {
             return false;
         }
-        List<PTable> physicalTableIndexes = physicalTable.getIndexes();
-        if (physicalTableIndexes.isEmpty()) {
+        List<PTable> parentTableIndexes = parentTable.getIndexes();
+        if (parentTableIndexes.isEmpty()) {
             return false;
         }
         // Filter out indexes if column doesn't exist in view
-        List<PTable> indexesToAdd = 
Lists.newArrayListWithExpectedSize(physicalTableIndexes.size() + 
view.getIndexes().size());
+        List<PTable> indexesToAdd = 
Lists.newArrayListWithExpectedSize(parentTableIndexes.size() + 
view.getIndexes().size());
         if (result.wasUpdated()) { // Table from server never contains 
inherited indexes
             indexesToAdd.addAll(view.getIndexes());
         } else { // Only add original ones, as inherited ones may have changed
@@ -697,11 +698,11 @@ public class MetaDataClient {
                 }
             }
         }
-        for (PTable index : physicalTableIndexes) {
+        for (PTable index : parentTableIndexes) {
             boolean containsAllReqdCols = true;
             // Ensure that all columns required to create index exist in the 
view too,
             // since view columns may be removed.
-            IndexMaintainer indexMaintainer = 
index.getIndexMaintainer(physicalTable, connection);
+            IndexMaintainer indexMaintainer = 
index.getIndexMaintainer(parentTable, connection);
             // Check that the columns required for the index pk are present in 
the view
             Set<ColumnReference> indexColRefs = 
indexMaintainer.getIndexedColumns();
             for (ColumnReference colRef : indexColRefs) {
@@ -720,7 +721,7 @@ public class MetaDataClient {
                 }
             }
             // Ensure that constant columns (i.e. columns matched in the view 
WHERE clause)
-            // all exist in the index on the physical table.
+            // all exist in the index on the parent table. 
             for (PColumn col : view.getColumns()) {
                 if (col.getViewConstant() != null) {
                     try {
@@ -729,17 +730,36 @@ public class MetaDataClient {
                         // would fail to compile.
                         String indexColumnName = 
IndexUtil.getIndexColumnName(col);
                         index.getColumn(indexColumnName);
-                    } catch (ColumnNotFoundException e) { // Ignore this index 
and continue with others
-                        containsAllReqdCols = false;
-                        break;
+                    } catch (ColumnNotFoundException e1) { 
+                        PColumn indexCol = null;
+                        try {
+                            String cf = col.getFamilyName()!=null ? 
col.getFamilyName().getString() : null;
+                            String cq = col.getName().getString();
+                            if (cf!=null) {
+                                indexCol = 
parentTable.getColumnFamily(cf).getColumn(cq);
+                            }
+                            else {
+                                indexCol = parentTable.getColumn(cq);
+                            }
+                        } catch (ColumnNotFoundException e2) { // Ignore this 
index and continue with others
+                            containsAllReqdCols = false;
+                            break;
+                        }
+                        if (indexCol.getViewConstant()==null || 
Bytes.compareTo(indexCol.getViewConstant(), col.getViewConstant())!=0) {
+                            containsAllReqdCols = false;
+                            break;
+                        }
                     }
                 }
             }
             if (containsAllReqdCols) {
                 // Tack on view statement to index to get proper filtering for 
view
-                String viewStatement = 
IndexUtil.rewriteViewStatement(connection, index, physicalTable, 
view.getViewStatement());
-                index = PTableImpl.makePTable(index, viewStatement);
-                indexesToAdd.add(index);
+                String viewStatement = 
IndexUtil.rewriteViewStatement(connection, index, parentTable, 
view.getViewStatement());
+                PName modifiedIndexName = 
PNameFactory.newName(index.getSchemaName().getString() + 
QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR 
+                    + index.getName().getString() + 
QueryConstants.CHILD_VIEW_INDEX_NAME_SEPARATOR + view.getName().getString());
+                // add the index table with a new name so that it does not 
conflict with the existing index table 
+                // also set update cache frequency to never since the renamed 
index is not present on the server 
+                indexesToAdd.add(PTableImpl.makePTable(index, 
modifiedIndexName, viewStatement, Long.MAX_VALUE, view.getTenantId()));
             }
         }
         PTable allIndexesTable = PTableImpl.makePTable(view, 
view.getTimeStamp(), indexesToAdd);
@@ -2190,7 +2210,6 @@ public class MetaDataClient {
             // add the columns in reverse order since we reverse the list later
             Collections.reverse(columnMetadata);
             tableMetaData.addAll(columnMetadata);
-
             String dataTableName = parent == null || tableType == 
PTableType.VIEW ? null : parent.getTableName().getString();
             PIndexState indexState = parent == null || tableType == 
PTableType.VIEW  ? null : PIndexState.BUILDING;
             PreparedStatement tableUpsert = 
connection.prepareStatement(CREATE_TABLE);
@@ -2341,7 +2360,7 @@ public class MetaDataClient {
                 PTable table =  PTableImpl.makePTable(
                         tenantId, newSchemaName, 
PNameFactory.newName(tableName), tableType, indexState, timestamp!=null ? 
timestamp : result.getMutationTime(),
                         PTable.INITIAL_SEQ_NUM, pkName == null ? null : 
PNameFactory.newName(pkName), saltBucketNum, columns.values(),
-                        dataTableName == null ? null : newSchemaName, 
dataTableName == null ? null : PNameFactory.newName(dataTableName), 
Collections.<PTable>emptyList(), isImmutableRows,
+                        parent == null ? null : parent.getSchemaName(), parent 
== null ? null : parent.getTableName(), Collections.<PTable>emptyList(), 
isImmutableRows,
                         physicalNames, defaultFamilyName == null ? null : 
PNameFactory.newName(defaultFamilyName), viewStatement, 
Boolean.TRUE.equals(disableWAL), multiTenant, storeNulls, viewType,
                         indexId, indexType, rowKeyOrderOptimizable, 
transactional, updateCacheFrequency, 0L, isNamespaceMapped, autoPartitionSeq, 
isAppendOnlySchema);
                 result = new MetaDataMutationResult(code, 
result.getMutationTime(), table, true);
@@ -3513,7 +3532,7 @@ public class MetaDataClient {
     }
 
     private PTable addTableToCache(MetaDataMutationResult result) throws 
SQLException {
-        addIndexesFromPhysicalTable(result, null);
+        addIndexesFromParentTable(result, null);
         PTable table = result.getTable();
         connection.addTable(table, TransactionUtil.getResolvedTime(connection, 
result));
         return table;

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
index 7a78006..8d7161e 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PMetaDataImpl.java
@@ -104,7 +104,7 @@ public class PMetaDataImpl implements PMetaData {
         PTable newParentTable = null;
         PTableRef newParentTableRef = null;
         long parentResolvedTimestamp = resolvedTime;
-        if (table.getParentName() != null) { // Upsert new index table into 
parent data table list
+        if (table.getType() == PTableType.INDEX) { // Upsert new index table 
into parent data table list
             String parentName = table.getParentName().getString();
             PTableRef oldParentRef = metaData.get(new 
PTableKey(table.getTenantId(), parentName));
             // If parentTable isn't cached, that's ok we can skip this

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
----------------------------------------------------------------------
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
index 344dc2c..b585323 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -24,8 +24,6 @@ import org.apache.hadoop.hbase.util.Bytes;
 import org.apache.phoenix.hbase.index.util.KeyValueBuilder;
 import org.apache.phoenix.index.IndexMaintainer;
 import org.apache.phoenix.jdbc.PhoenixConnection;
-import org.apache.phoenix.query.ConnectionQueryServices;
-import org.apache.phoenix.schema.stats.PTableStats;
 
 
 /**
@@ -281,21 +279,21 @@ public interface PTable extends PMetaDataEntity {
     PIndexState getIndexState();
 
     /**
-     * Gets the full name of the data table for an index table.
-     * @return the name of the data table that this index is on
-     *  or null if not an index.
+     * @return the full name of the parent view for a view or data table for 
an index table 
+     * or null if this is not a view or index table. Also returns null for a 
view of a data table 
+     * (use @getPhysicalName for this case) 
      */
     PName getParentName();
     /**
-     * Gets the table name of the data table for an index table.
-     * @return the table name of the data table that this index is
-     * on or null if not an index.
+     * @return the table name of the parent view for a view or data table for 
an index table 
+     * or null if this is not a view or index table. Also returns null for a 
view of a data table 
+     * (use @getPhysicalTableName for this case) 
      */
     PName getParentTableName();
     /**
-     * Gets the schema name of the data table for an index table.
-     * @return the schema name of the data table that this index is
-     * on or null if not an index.
+     * @return the schema name of the parent view for a view or data table for 
an index table 
+     * or null if this is not a view or index table. Also returns null for 
view of a data table 
+     * (use @getPhysicalSchemaName for this case) 
      */
     PName getParentSchemaName();
 

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java 
b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index b9cec02..293d422 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -206,12 +206,22 @@ public class PTableImpl implements PTable {
     }
 
     public static PTableImpl makePTable(PTable table, long timeStamp, 
List<PTable> indexes) throws SQLException {
-        return makePTable(table, timeStamp, indexes, table.getSchemaName(), 
table.getViewStatement());
+        return makePTable(table, timeStamp, indexes, 
table.getParentSchemaName(), table.getViewStatement());
     }
 
-    public static PTable makePTable(PTable table, String viewStatement) throws 
SQLException {
-        return Objects.equal(viewStatement, table.getViewStatement()) ? table 
: makePTable(table, table.getTimeStamp(), table.getIndexes(), 
table.getSchemaName(), viewStatement);
+    public static PTable makePTable(PTable index, PName indexName, String 
viewStatement, long updateCacheFrequency, PName tenantId) throws SQLException {
+        return Objects.equal(viewStatement, index.getViewStatement()) ? index 
: makePTable(index, indexName, index.getTimeStamp(), 
Lists.newArrayList(index.getPhysicalName()), index.getIndexes(), viewStatement, 
updateCacheFrequency, tenantId);
     }
+    
+    public static PTableImpl makePTable(PTable table, PName tableName, long 
timeStamp, List<PName> physicalNames, List<PTable> indexes, String 
viewStatement, long updateCacheFrequency, PName tenantId) throws SQLException {
+            return new PTableImpl(
+                    tenantId, table.getSchemaName(), tableName, 
table.getType(), table.getIndexState(), timeStamp,
+                    table.getSequenceNumber(), table.getPKName(), 
table.getBucketNum(), getColumnsToClone(table), table.getParentSchemaName(), 
table.getParentTableName(),
+                    indexes, table.isImmutableRows(), physicalNames, 
table.getDefaultFamilyName(), viewStatement,
+                    table.isWALDisabled(), table.isMultiTenant(), 
table.getStoreNulls(), table.getViewType(), table.getViewIndexId(), 
table.getIndexType(),
+                    table.getBaseColumnCount(), 
table.rowKeyOrderOptimizable(), table.isTransactional(), updateCacheFrequency,
+                    table.getIndexDisableTimestamp(), 
table.isNamespaceMapped(), table.getAutoPartitionSeqName(), 
table.isAppendOnlySchema());
+        }
 
     public static PTableImpl makePTable(PTable table, long timeStamp, 
List<PTable> indexes, PName parentSchemaName, String viewStatement) throws 
SQLException {
         return new PTableImpl(
@@ -335,7 +345,7 @@ public class PTableImpl implements PTable {
             int baseColumnCount, boolean rowKeyOrderOptimizable, boolean 
isTransactional, long updateCacheFrequency,
             long indexDisableTimestamp, boolean isNamespaceMapped, String 
autoPartitionSeqName, boolean isAppendOnlySchema) throws SQLException {
         init(tenantId, schemaName, tableName, type, state, timeStamp, 
sequenceNumber, pkName, bucketNum, columns,
-                schemaName, parentTableName, indexes, isImmutableRows, 
physicalNames, defaultFamilyName,
+                parentSchemaName, parentTableName, indexes, isImmutableRows, 
physicalNames, defaultFamilyName,
                 viewExpression, disableWAL, multiTenant, storeNulls, viewType, 
viewIndexId, indexType, baseColumnCount, rowKeyOrderOptimizable,
                 isTransactional, updateCacheFrequency, indexDisableTimestamp, 
isNamespaceMapped, autoPartitionSeqName, isAppendOnlySchema);
     }
@@ -519,7 +529,7 @@ public class PTableImpl implements PTable {
         this.parentSchemaName = parentSchemaName;
         this.parentTableName = parentTableName;
         this.parentName = parentTableName == null ? null : 
PNameFactory.newName(SchemaUtil.getTableName(
-                parentSchemaName.getString(), parentTableName.getString()));
+            parentSchemaName!=null ? parentSchemaName.getString() : null, 
parentTableName.getString()));
         estimatedSize += PNameFactory.getEstimatedSize(this.parentName);
 
         this.physicalNames = physicalNames == null ? ImmutableList.<PName>of() 
: ImmutableList.copyOf(physicalNames);
@@ -945,12 +955,15 @@ public class PTableImpl implements PTable {
 
     @Override
     public PName getParentTableName() {
-        return parentTableName;
+        // a view on a table will not have a parent name but will have a 
physical table name (which is the parent)
+        return (type!=PTableType.VIEW || parentName!=null) ? parentTableName : 
+            
PNameFactory.newName(SchemaUtil.getTableNameFromFullName(getPhysicalName().getBytes()));
     }
 
     @Override
     public PName getParentName() {
-        return parentName;
+        // a view on a table will not have a parent name but will have a 
physical table name (which is the parent)
+        return (type!=PTableType.VIEW || parentName!=null) ? parentName : 
getPhysicalName();
     }
 
     @Override
@@ -1058,9 +1071,11 @@ public class PTableImpl implements PTable {
       }
 
       boolean isImmutableRows = table.getIsImmutableRows();
-      PName dataTableName = null;
+      PName parentSchemaName = null;
+      PName parentTableName = null;
       if (table.hasDataTableNameBytes()) {
-        dataTableName = 
PNameFactory.newName(table.getDataTableNameBytes().toByteArray());
+        parentSchemaName = 
PNameFactory.newName(SchemaUtil.getSchemaNameFromFullName((table.getDataTableNameBytes().toByteArray())));
+        parentTableName = 
PNameFactory.newName(SchemaUtil.getTableNameFromFullName(table.getDataTableNameBytes().toByteArray()));
       }
       PName defaultFamilyName = null;
       if (table.hasDefaultFamilyName()) {
@@ -1115,7 +1130,7 @@ public class PTableImpl implements PTable {
       try {
         PTableImpl result = new PTableImpl();
         result.init(tenantId, schemaName, tableName, tableType, indexState, 
timeStamp, sequenceNumber, pkName,
-            (bucketNum == NO_SALTING) ? null : bucketNum, columns, 
schemaName,dataTableName, indexes,
+            (bucketNum == NO_SALTING) ? null : bucketNum, columns, 
parentSchemaName, parentTableName, indexes,
             isImmutableRows, physicalNames, defaultFamilyName, viewStatement, 
disableWAL,
             multiTenant, storeNulls, viewType, viewIndexId, indexType, 
baseColumnCount, rowKeyOrderOptimizable,
             isTransactional, updateCacheFrequency, indexDisableTimestamp, 
isNamespaceMapped, autoParititonSeqName, isAppendOnlySchema);
@@ -1172,7 +1187,7 @@ public class PTableImpl implements PTable {
       builder.setIsImmutableRows(table.isImmutableRows());
 
       if (table.getParentName() != null) {
-        
builder.setDataTableNameBytes(ByteStringer.wrap(table.getParentTableName().getBytes()));
+        
builder.setDataTableNameBytes(ByteStringer.wrap(table.getParentName().getBytes()));
       }
       if (table.getDefaultFamilyName()!= null) {
         
builder.setDefaultFamilyName(ByteStringer.wrap(table.getDefaultFamilyName().getBytes()));
@@ -1211,7 +1226,9 @@ public class PTableImpl implements PTable {
 
     @Override
     public PName getParentSchemaName() {
-        return parentSchemaName;
+        // a view on a table will not have a parent name but will have a 
physical table name (which is the parent)
+        return (type!=PTableType.VIEW || parentName!=null) ? parentSchemaName 
: 
+            
PNameFactory.newName(SchemaUtil.getSchemaNameFromFullName(getPhysicalName().getBytes()));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/phoenix/blob/5a6496e1/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
----------------------------------------------------------------------
diff --git 
a/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java 
b/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
index 6032aec..6f8b19f 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ExpressionUtil.java
@@ -10,34 +10,21 @@
 package org.apache.phoenix.util;
 
 import java.sql.SQLException;
-import java.util.List;
 
 import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
 import org.apache.phoenix.expression.Determinism;
 import org.apache.phoenix.expression.Expression;
 import org.apache.phoenix.expression.LiteralExpression;
-import org.apache.phoenix.expression.function.CurrentDateFunction;
-import org.apache.phoenix.expression.function.CurrentTimeFunction;
-import org.apache.phoenix.expression.function.FunctionExpression;
 import org.apache.phoenix.schema.types.PDataType;
 
-import com.google.common.collect.Lists;
-
 public class ExpressionUtil {
 
-       @SuppressWarnings("unchecked")
-       private static final List<Class<? extends FunctionExpression>> 
OVERRIDE_LITERAL_FUNCTIONS = Lists
-                       .<Class<? extends FunctionExpression>> newArrayList(
-                                       CurrentDateFunction.class, 
CurrentTimeFunction.class);
-
        private ExpressionUtil() {
        }
 
        public static boolean isConstant(Expression expression) {
                return (expression.isStateless() && 
(expression.getDeterminism() == Determinism.ALWAYS
-                               || expression.getDeterminism() == 
Determinism.PER_STATEMENT 
-                               // TODO remove this in 3.4/4.4 (need to support 
clients on 3.1/4.1)
-                               || 
OVERRIDE_LITERAL_FUNCTIONS.contains(expression.getClass())));
+                               || expression.getDeterminism() == 
Determinism.PER_STATEMENT));
        }
 
     public static LiteralExpression getConstantExpression(Expression 
expression, ImmutableBytesWritable ptr)

Reply via email to