This is an automated email from the ASF dual-hosted git repository.
shahrs87 pushed a commit to branch PHOENIX-6883-feature
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/PHOENIX-6883-feature by this
push:
new 39d7e94885 PHOENIX-7190 : Store LAST_DDL_TIMESTAMP of all ancestors in
a View's PTable (#1807)
39d7e94885 is described below
commit 39d7e94885cb5fc0e86e1d090d4398ade6a071fe
Author: palash <[email protected]>
AuthorDate: Thu Feb 8 14:43:51 2024 -0800
PHOENIX-7190 : Store LAST_DDL_TIMESTAMP of all ancestors in a View's PTable
(#1807)
---
.../org/apache/phoenix/schema/DelegateTable.java | 5 +
.../org/apache/phoenix/schema/MetaDataClient.java | 72 +++++++++--
.../java/org/apache/phoenix/schema/PTable.java | 4 +
.../java/org/apache/phoenix/schema/PTableImpl.java | 15 ++-
.../java/org/apache/phoenix/schema/PTableKey.java | 13 ++
.../phoenix/util/ValidateLastDDLTimestampUtil.java | 60 ++++-----
.../org/apache/phoenix/end2end/ViewMetadataIT.java | 117 ++++++++++++++++++
.../java/org/apache/phoenix/rpc/UpdateCacheIT.java | 2 +-
.../phoenix/cache/ServerMetadataCacheTest.java | 134 +++++++++++++++++++++
9 files changed, 371 insertions(+), 51 deletions(-)
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/DelegateTable.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/DelegateTable.java
index 1a5d83e728..453a7663d1 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/DelegateTable.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/DelegateTable.java
@@ -413,6 +413,11 @@ public class DelegateTable implements PTable {
return delegate.getIndexWhere();
}
+ @Override
+ public Map<PTableKey, Long> getAncestorLastDDLTimestampMap() {
+ return delegate.getAncestorLastDDLTimestampMap();
+ }
+
@Override
public Expression getIndexWhereExpression(PhoenixConnection connection)
throws SQLException {
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index 4878cb5cf4..0d63f089f5 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -716,8 +716,9 @@ 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 (addColumnsAndIndexesFromAncestors(result,
resolvedTimestamp,
- true, false)) {
+ if
(addColumnsIndexesAndLastDDLTimestampsFromAncestors(result,
+ resolvedTimestamp, true, false)) {
+ updateIndexesWithAncestorMap(result);
connection.addTable(result.getTable(),
resolvedTime);
} else {
// if we aren't adding the table, we still
need to update the
@@ -898,14 +899,14 @@ 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 addColumnsAndIndexesFromAncestors(MetaDataMutationResult
result, Long resolvedTimestamp,
- boolean
alwaysAddAncestorColumnsAndIndexes,
- boolean
alwaysHitServerForAncestors)
+ private boolean addColumnsIndexesAndLastDDLTimestampsFromAncestors(
+ MetaDataMutationResult result,
Long resolvedTimestamp,
+ boolean
alwaysAddAncestorColumnsAndIndexes,
+ boolean
alwaysHitServerForAncestors)
throws SQLException {
PTable table = result.getTable();
boolean hasIndexId = table.getViewIndexId() != null;
- // only need to inherit columns and indexes for view indexes and views
- if ((table.getType()==PTableType.INDEX && hasIndexId)
+ if (table.getType() == PTableType.INDEX
|| (table.getType() == PTableType.VIEW && table.getViewType()
!= ViewType.MAPPED)) {
String tableName = null;
try {
@@ -940,8 +941,18 @@ public class MetaDataClient {
if (!alwaysAddAncestorColumnsAndIndexes &&
!result.wasUpdated() && !parentResult.wasUpdated()) {
return false;
}
- result.setTable(ViewUtil.addDerivedColumnsAndIndexesFromParent(
- connection, table, parentTable));
+
+ // only need to inherit columns and indexes for view indexes
and views
+ if (!table.getType().equals(PTableType.INDEX) || hasIndexId) {
+ PTable pTableWithDerivedColumnsAndIndexes
+ =
ViewUtil.addDerivedColumnsAndIndexesFromParent(connection,
+ table, parentTable);
+ result.setTable(getPTableWithAncestorLastDDLTimestampMap(
+ pTableWithDerivedColumnsAndIndexes,
parentTable));
+ } else {
+ result.setTable(getPTableWithAncestorLastDDLTimestampMap(
+ table, parentTable));
+ }
return true;
} catch (Throwable e) {
TableMetricsManager.updateMetricsForSystemCatalogTableMethod(tableName,
NUM_METADATA_LOOKUP_FAILURES, 1);
@@ -951,6 +962,42 @@ public class MetaDataClient {
return false;
}
+ /**
+ * Update the indexes within this result's table with
ancestor->last_ddl_timestamp map.
+ */
+ private void updateIndexesWithAncestorMap(MetaDataMutationResult result)
throws SQLException {
+ PTable table = result.getTable();
+ if (table.getIndexes().isEmpty()) {
+ return;
+ }
+ List<PTable> newIndexes = new ArrayList<>(table.getIndexes().size());
+ for (PTable index : table.getIndexes()) {
+ newIndexes.add(getPTableWithAncestorLastDDLTimestampMap(index,
table));
+ }
+ result.setTable(PTableImpl.builderWithColumns(table,
PTableImpl.getColumnsToClone(table))
+ .setIndexes(newIndexes).build());
+ }
+
+ /**
+ * Creates a new PTable object from the provided pTable and with the
ancestorLastDDLTimestampMap
+ * Copy the map of the parent and add the last_ddl_timestamp of the parent
in the map.
+ * @param pTable
+ * @param parentTable
+ */
+ private PTable getPTableWithAncestorLastDDLTimestampMap(PTable pTable,
PTable parentTable)
+ throws SQLException {
+ Map<PTableKey, Long> ancestorMap
+ = new HashMap<>(parentTable.getAncestorLastDDLTimestampMap());
+ // this method can be called for an index and a view which inherited
this index
+ // from its ancestors, skip adding the view as an ancestor of the
index.
+ if (pTable.getParentName().equals(parentTable.getName())) {
+ ancestorMap.put(parentTable.getKey(),
parentTable.getLastDDLTimestamp());
+ }
+ return PTableImpl.builderWithColumns(pTable,
PTableImpl.getColumnsToClone(pTable))
+ .setAncestorLastDDLTimestampMap(ancestorMap)
+ .build();
+ }
+
private void addFunctionArgMutation(String functionName,
FunctionArgument arg, PreparedStatement argUpsert, int position) throws
SQLException {
argUpsert.setString(1, connection.getTenantId() == null ? null :
connection.getTenantId().getString());
argUpsert.setString(2, functionName);
@@ -5163,9 +5210,10 @@ public class MetaDataClient {
private void addTableToCache(MetaDataMutationResult result, boolean
alwaysHitServerForAncestors,
long timestamp) throws SQLException {
- addColumnsAndIndexesFromAncestors(result, null, false,
alwaysHitServerForAncestors);
- PTable table = result.getTable();
- connection.addTable(table, timestamp);
+ addColumnsIndexesAndLastDDLTimestampsFromAncestors(result, null,
+ false, alwaysHitServerForAncestors);
+ updateIndexesWithAncestorMap(result);
+ connection.addTable(result.getTable(), timestamp);
}
private void addFunctionToCache(MetaDataMutationResult result) throws
SQLException {
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
index 6fd89ad4bb..20f03fb621 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTable.java
@@ -981,6 +981,10 @@ public interface PTable extends PMetaDataEntity {
*/
String getIndexWhere();
+ /**
+ * @return the map of all ancestors to their LAST_DDL_TIMESTAMP
+ */
+ Map<PTableKey, Long> getAncestorLastDDLTimestampMap();
/**
*
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableImpl.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableImpl.java
index b5c2eee2e7..2885de65ea 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableImpl.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableImpl.java
@@ -218,6 +218,7 @@ public class PTableImpl implements PTable {
private String indexWhere;
private Expression indexWhereExpression;
private Set<ColumnReference> indexWhereColumns;
+ private Map<PTableKey, Long> ancestorLastDDLTimestampMap;
public static class Builder {
private PTableKey key;
@@ -284,6 +285,7 @@ public class PTableImpl implements PTable {
private String externalSchemaId;
private String streamingTopicName;
private String indexWhere;
+ private Map<PTableKey, Long> ancestorLastDDLTimestampMap = new
HashMap<>();
// Used to denote which properties a view has explicitly modified
private BitSet viewModifiedPropSet = new BitSet(3);
@@ -711,6 +713,10 @@ public class PTableImpl implements PTable {
return this;
}
+ public Builder setAncestorLastDDLTimestampMap(Map<PTableKey, Long>
map) {
+ this.ancestorLastDDLTimestampMap = map;
+ return this;
+ }
/**
* Populate derivable attributes of the PTable
* @return PTableImpl.Builder object
@@ -1002,6 +1008,7 @@ public class PTableImpl implements PTable {
this.externalSchemaId = builder.externalSchemaId;
this.streamingTopicName = builder.streamingTopicName;
this.indexWhere = builder.indexWhere;
+ this.ancestorLastDDLTimestampMap = builder.ancestorLastDDLTimestampMap;
}
// When cloning table, ignore the salt column as it will be added back in
the constructor
@@ -1082,7 +1089,8 @@ public class PTableImpl implements PTable {
.setSchemaVersion(table.getSchemaVersion())
.setExternalSchemaId(table.getExternalSchemaId())
.setStreamingTopicName(table.getStreamingTopicName())
- .setIndexWhere(table.getIndexWhere());
+ .setIndexWhere(table.getIndexWhere())
+
.setAncestorLastDDLTimestampMap(table.getAncestorLastDDLTimestampMap());
}
@Override
@@ -2376,6 +2384,11 @@ public class PTableImpl implements PTable {
return indexWhere;
}
+ @Override
+ public Map<PTableKey, Long> getAncestorLastDDLTimestampMap() {
+ return ancestorLastDDLTimestampMap;
+ }
+
private void buildIndexWhereExpression(PhoenixConnection connection)
throws SQLException {
PhoenixPreparedStatement
pstmt =
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableKey.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableKey.java
index 874b62f3a8..a0204b8541 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableKey.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/PTableKey.java
@@ -20,10 +20,13 @@ package org.apache.phoenix.schema;
import org.apache.phoenix.query.QueryConstants;
import org.apache.phoenix.thirdparty.com.google.common.base.Preconditions;
+import org.apache.phoenix.util.SchemaUtil;
public class PTableKey {
private final PName tenantId;
private final String name;
+ private final String schemaName;
+ private final String tableName;
public PTableKey(PName tenantId, String name) {
Preconditions.checkNotNull(name);
@@ -33,6 +36,8 @@ public class PTableKey {
} else {
this.name = name;
}
+ this.schemaName = SchemaUtil.getSchemaNameFromFullName(this.name);
+ this.tableName = SchemaUtil.getTableNameFromFullName(this.name);
}
public PName getTenantId() {
@@ -42,6 +47,14 @@ public class PTableKey {
public String getName() {
return name;
}
+
+ public String getSchemaName() {
+ return schemaName;
+ }
+
+ public String getTableName() {
+ return tableName;
+ }
@Override
public String toString() {
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/ValidateLastDDLTimestampUtil.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/ValidateLastDDLTimestampUtil.java
index 85acfe0753..01d58f2f11 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/util/ValidateLastDDLTimestampUtil.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/util/ValidateLastDDLTimestampUtil.java
@@ -19,6 +19,7 @@ package org.apache.phoenix.util;
import java.sql.SQLException;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.ThreadLocalRandom;
import org.apache.hadoop.hbase.HConstants;
@@ -33,8 +34,6 @@ import org.apache.phoenix.query.QueryServicesOptions;
import org.apache.phoenix.schema.PName;
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.schema.TableRef;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -135,7 +134,7 @@ public class ValidateLastDDLTimestampUtil {
*/
private static RegionServerEndpointProtos.ValidateLastDDLTimestampRequest
getValidateDDLTimestampRequest(PhoenixConnection conn, List<TableRef>
tableRefs,
- boolean isWritePath) throws
TableNotFoundException {
+ boolean isWritePath) {
RegionServerEndpointProtos.ValidateLastDDLTimestampRequest.Builder
requestBuilder
=
RegionServerEndpointProtos.ValidateLastDDLTimestampRequest.newBuilder();
@@ -143,45 +142,32 @@ public class ValidateLastDDLTimestampUtil {
for (TableRef tableRef : tableRefs) {
- //when querying an index, we need to validate its parent table
- //in case the index was dropped
- if (PTableType.INDEX.equals(tableRef.getTable().getType())) {
+ // validate all ancestors of this PTable if any
+ // index -> base table
+ // view -> parent view and its ancestors
+ // view index -> view and its ancestors
+ for (Map.Entry<PTableKey, Long> entry
+ :
tableRef.getTable().getAncestorLastDDLTimestampMap().entrySet()) {
innerBuilder =
RegionServerEndpointProtos.LastDDLTimestampRequest.newBuilder();
- PTableKey key = new PTableKey(conn.getTenantId(),
- tableRef.getTable().getParentName().getString());
- PTable parentTable = conn.getTable(key);
- setLastDDLTimestampRequestParameters(innerBuilder,
conn.getTenantId(), parentTable);
+ PTableKey ancestorKey = entry.getKey();
+ setLastDDLTimestampRequestParameters(innerBuilder,
ancestorKey, entry.getValue());
requestBuilder.addLastDDLTimestampRequests(innerBuilder);
}
- // add the tableRef to the request
+ // add the current table to the request
+ PTable ptable = tableRef.getTable();
innerBuilder =
RegionServerEndpointProtos.LastDDLTimestampRequest.newBuilder();
- setLastDDLTimestampRequestParameters(
- innerBuilder, conn.getTenantId(), tableRef.getTable());
+ setLastDDLTimestampRequestParameters(innerBuilder, ptable.getKey(),
+
ptable.getLastDDLTimestamp());
requestBuilder.addLastDDLTimestampRequests(innerBuilder);
- //when querying a view, we need to validate last ddl timestamps
for all its ancestors
- if (PTableType.VIEW.equals(tableRef.getTable().getType())) {
- PTable pTable = tableRef.getTable();
- while (pTable.getParentName() != null) {
- PTableKey key = new PTableKey(conn.getTenantId(),
- pTable.getParentName().getString());
- PTable parentTable = conn.getTable(key);
- innerBuilder =
RegionServerEndpointProtos.LastDDLTimestampRequest.newBuilder();
- setLastDDLTimestampRequestParameters(
- innerBuilder, conn.getTenantId(), parentTable);
- requestBuilder.addLastDDLTimestampRequests(innerBuilder);
- pTable = parentTable;
- }
- }
-
//on the write path, we need to validate all indexes of a
table/view
//in case index state was changed
if (isWritePath) {
for (PTable idxPTable : tableRef.getTable().getIndexes()) {
innerBuilder =
RegionServerEndpointProtos.LastDDLTimestampRequest.newBuilder();
- setLastDDLTimestampRequestParameters(
- innerBuilder, conn.getTenantId(), idxPTable);
+ setLastDDLTimestampRequestParameters(innerBuilder,
idxPTable.getKey(),
+
idxPTable.getLastDDLTimestamp());
requestBuilder.addLastDDLTimestampRequests(innerBuilder);
}
}
@@ -194,16 +180,16 @@ public class ValidateLastDDLTimestampUtil {
*/
private static void setLastDDLTimestampRequestParameters(
RegionServerEndpointProtos.LastDDLTimestampRequest.Builder builder,
- PName tenantId, PTable pTable) {
- byte[] tenantIDBytes = tenantId == null
+ PTableKey key, long lastDDLTimestamp) {
+ byte[] tenantIDBytes = key.getTenantId() == null
? HConstants.EMPTY_BYTE_ARRAY
- : tenantId.getBytes();
- byte[] schemaBytes = pTable.getSchemaName() == null
+ : key.getTenantId().getBytes();
+ byte[] schemaBytes = key.getSchemaName() == null
? HConstants.EMPTY_BYTE_ARRAY
- : pTable.getSchemaName().getBytes();
+ : key.getSchemaName().getBytes();
builder.setTenantId(ByteStringer.wrap(tenantIDBytes));
builder.setSchemaName(ByteStringer.wrap(schemaBytes));
-
builder.setTableName(ByteStringer.wrap(pTable.getTableName().getBytes()));
- builder.setLastDDLTimestamp(pTable.getLastDDLTimestamp());
+ builder.setTableName(ByteStringer.wrap(key.getTableName().getBytes()));
+ builder.setLastDDLTimestamp(lastDDLTimestamp);
}
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewMetadataIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewMetadataIT.java
index 5c59eb3939..95cb40d8cd 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewMetadataIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewMetadataIT.java
@@ -82,7 +82,10 @@ import org.apache.phoenix.query.QueryServices;
import org.apache.phoenix.schema.ColumnAlreadyExistsException;
import org.apache.phoenix.schema.PColumn;
import org.apache.phoenix.schema.PName;
+import org.apache.phoenix.schema.PNameImpl;
import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableImpl;
+import org.apache.phoenix.schema.PTableKey;
import org.apache.phoenix.schema.PTableType;
import org.apache.phoenix.schema.TableAlreadyExistsException;
import org.apache.phoenix.schema.TableNotFoundException;
@@ -1371,6 +1374,120 @@ public class ViewMetadataIT extends
SplitSystemCatalogIT {
assertPKs(rs, new String[] {"K1", "K2", "K3", "K4"});
}
+ @Test
+ public void testAncestorLastDDLMapPopulatedInViewAndIndexHierarchy()
throws SQLException {
+ String baseTable = SchemaUtil.getTableName(SCHEMA1,
generateUniqueName());
+ String view1 = SchemaUtil.getTableName(SCHEMA2, generateUniqueName());
+ String view2 = SchemaUtil.getTableName(SCHEMA3, generateUniqueName());
+ String view3 = SchemaUtil.getTableName(SCHEMA4, generateUniqueName());
+ String view4 = SchemaUtil.getTableName(SCHEMA2, generateUniqueName());
+ String index1 = generateUniqueName();
+ String index2 = generateUniqueName();
+ String tenant1 = TENANT1;
+ String tenant2 = TENANT2;
+ /* baseTable
+ / | \
\
+ view1(tenant1) view3(tenant2) index1(global)
view4(global)
+ /
+ view2(tenant1)
+ /
+ index2(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);
+ String index1DDL = "CREATE INDEX " + index1 + " ON " + baseTable +
"(V1)";
+ conn.createStatement().execute(index1DDL);
+
+
+ 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);
+
+ String index2DDL = "CREATE INDEX " + index2 + " ON " + view2 +
"(V1)";
+ tenant1Conn.createStatement().execute(index2DDL);
+ }
+
+ 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);
+
+ //validate ancestor->last_ddl_timestamps maps
+ PTable basePTable = PhoenixRuntime.getTable(conn, baseTable);
+ Long baseTableLastDDLTimestamp = basePTable.getLastDDLTimestamp();
+ PTableKey baseTableKey = new PTableKey(null, baseTable);
+ //base table map should be empty
+ Map<PTableKey,Long> map =
basePTable.getAncestorLastDDLTimestampMap();
+ assertEquals(0, map.size());
+
+ //global view
+ map = PhoenixRuntime.getTable(conn,
view4).getAncestorLastDDLTimestampMap();
+ assertEquals(1, map.size());
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+
+ //global index in cache and in parent PTable
+ PTable index1PTable = PhoenixRuntime.getTable(conn,
SchemaUtil.getTableName(SCHEMA1, index1));
+ map = index1PTable.getAncestorLastDDLTimestampMap();
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+ assertEquals(1, basePTable.getIndexes().size());
+ map =
basePTable.getIndexes().get(0).getAncestorLastDDLTimestampMap();
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+
+ //tenant2 view
+ try (Connection tenant2Conn = getTenantConnection(tenant2)) {
+ map = PhoenixRuntime.getTable(tenant2Conn,
view3).getAncestorLastDDLTimestampMap();
+ assertEquals(1, map.size());
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+ }
+ try (Connection tenant1Conn = getTenantConnection(tenant1)) {
+ //tenant1 view
+ PTable view1PTable = PhoenixRuntime.getTable(tenant1Conn,
view1);
+ map = view1PTable.getAncestorLastDDLTimestampMap();
+ assertEquals(1, map.size());
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+ //tenant1 child view
+ PTableKey view1Key = new PTableKey(view1PTable.getTenantId(),
view1);
+ map = PhoenixRuntime.getTable(tenant1Conn,
view2).getAncestorLastDDLTimestampMap();
+ assertEquals(2, map.size());
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+ assertEquals(view1PTable.getLastDDLTimestamp(),
map.get(view1Key));
+ //tenant1 child view index in cache and in child view PTable
+ PTable view2PTable = PhoenixRuntime.getTable(tenant1Conn,
view2);
+ PTableKey view2Key = new PTableKey(view2PTable.getTenantId(),
view2);
+ PTable index2PTable = PhoenixRuntime.getTable(tenant1Conn,
SchemaUtil.getTableName(SCHEMA3, index2));
+ map = index2PTable.getAncestorLastDDLTimestampMap();
+ assertEquals(baseTableLastDDLTimestamp, map.get(baseTableKey));
+ assertEquals(view2PTable.getLastDDLTimestamp(),
map.get(view2Key));
+ assertEquals(2, view2PTable.getIndexes().size());
+ for (PTable index : view2PTable.getIndexes()) {
+ // inherited index
+ if (index.getTableName().getString().equals(index1)) {
+ map = index.getAncestorLastDDLTimestampMap();
+ assertEquals(baseTableLastDDLTimestamp,
map.get(baseTableKey));
+ } else {
+ // view index
+ map = index.getAncestorLastDDLTimestampMap();
+ assertEquals(baseTableLastDDLTimestamp,
map.get(baseTableKey));
+ assertEquals(view2PTable.getLastDDLTimestamp(),
map.get(view2Key));
+ }
+ }
+ }
+ }
+ }
+
+ private Connection getTenantConnection(String tenantId) throws
SQLException {
+ Properties tenantProps = new Properties();
+ tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId);
+ return DriverManager.getConnection(getUrl(), tenantProps);
+ }
+
private void assertPKs(ResultSet rs, String[] expectedPKs)
throws SQLException {
List<String> pkCols = newArrayListWithExpectedSize(expectedPKs.length);
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
index 93cd74947a..7be7d1c715 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/rpc/UpdateCacheIT.java
@@ -231,7 +231,7 @@ public class UpdateCacheIT extends ParallelStatsDisabledIT {
// Even the indexes should now have the modified value of
UPDATE_CACHE_FREQUENCY
// Note that when we query the base table, during query plan
generation, we make 2 getTable
// requests (to retrieve the base table) for each index of the base
table
- helpTestUpdateCache(fullTableName, new int[] {1, 18}, false);
+ helpTestUpdateCache(fullTableName, new int[] {1, 21}, false);
helpTestUpdateCache(INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR
+ localIndex,
new int[] {3}, true);
helpTestUpdateCache(INDEX_DATA_SCHEMA + QueryConstants.NAME_SEPARATOR
+ globalIndex,
diff --git
a/phoenix-core/src/test/java/org/apache/phoenix/cache/ServerMetadataCacheTest.java
b/phoenix-core/src/test/java/org/apache/phoenix/cache/ServerMetadataCacheTest.java
index f857b670c8..ec70879b17 100644
---
a/phoenix-core/src/test/java/org/apache/phoenix/cache/ServerMetadataCacheTest.java
+++
b/phoenix-core/src/test/java/org/apache/phoenix/cache/ServerMetadataCacheTest.java
@@ -31,6 +31,7 @@ import org.apache.phoenix.schema.ColumnNotFoundException;
import org.apache.phoenix.schema.ConnectionProperty;
import org.apache.phoenix.schema.PIndexState;
import org.apache.phoenix.schema.PTable;
+import org.apache.phoenix.schema.PTableKey;
import org.apache.phoenix.schema.TableNotFoundException;
import org.apache.phoenix.schema.types.PVarchar;
import org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
@@ -1458,6 +1459,139 @@ public class ServerMetadataCacheTest extends
ParallelStatsDisabledIT {
}
}
+ /**
+ * Test that a query on the column of a view which was previously dropped
+ * throws a ColumnNotFoundException. Use the same client to drop the
column.
+ */
+ @Test
+ public void testDroppedTableColumnNotVisibleToViewUsingSameClient() throws
Exception {
+ testDroppedTableColumnNotVisibleToView(true);
+ }
+
+ /**
+ * Test that a query on the column of a view which was previously dropped
+ * throws a ColumnNotFoundException. Use a different client to drop the
column.
+ */
+ @Test
+ public void testDroppedTableColumnNotVisibleToViewUsingDifferentClients()
throws Exception {
+ testDroppedTableColumnNotVisibleToView(false);
+ }
+
+ public void testDroppedTableColumnNotVisibleToView(boolean useSameClient)
throws Exception {
+ Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+ String url1 = QueryUtil.getConnectionUrl(props, config, "client1");
+ String url2 = QueryUtil.getConnectionUrl(props, config, "client2");
+ String tableName = generateUniqueName();
+ String viewName1 = generateUniqueName();
+ String viewName2 = generateUniqueName();
+ ConnectionQueryServices cqs1 = driver.getConnectionQueryServices(url1,
props);
+ ConnectionQueryServices cqs2 = driver.getConnectionQueryServices(url2,
props);
+ try (Connection conn = cqs1.connect(url1, props);
+ Connection conn2 = useSameClient ? conn : cqs2.connect(url2,
props)) {
+ createTable(conn, tableName, NEVER);
+ createView(conn, tableName, viewName1);
+ createView(conn, viewName1, viewName2);
+ query(conn2, viewName2);
+
+ alterTableDropColumn(conn, tableName, "v2");
+ query(conn2, tableName);
+
+ conn2.createStatement().execute("SELECT v2 FROM " + viewName2);
+ fail("Column dropped from base table should not be visible to
view.");
+ } catch (ColumnNotFoundException expected) {
+ }
+ }
+
+ /**
+ * Test that ancestor->last_ddl_timestamp is populated in a new client.
+ * @throws Exception
+ */
+ @Test
+ public void testAncestorLastDDLMapPopulatedInDifferentClient() throws
Exception {
+ String SCHEMA1 = generateUniqueName();
+ String SCHEMA2 = generateUniqueName();
+ Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);
+ String baseTable = SchemaUtil.getTableName(SCHEMA1,
generateUniqueName());
+ String index = generateUniqueName();
+ String view = SchemaUtil.getTableName(SCHEMA2, generateUniqueName());
+ String viewIndex = generateUniqueName();
+ String baseTable2 = SchemaUtil.getTableName(SCHEMA1,
generateUniqueName());
+ String index2 = generateUniqueName();
+ String view2 = SchemaUtil.getTableName(SCHEMA2, generateUniqueName());
+ String viewIndex2 = generateUniqueName();
+ String url1 = QueryUtil.getConnectionUrl(props, config, "client1");
+ String url2 = QueryUtil.getConnectionUrl(props, config, "client2");
+ ConnectionQueryServices cqs1 = driver.getConnectionQueryServices(url1,
props);
+ ConnectionQueryServices cqs2 = driver.getConnectionQueryServices(url2,
props);
+ try (Connection conn = cqs1.connect(url1, props);
+ Connection conn2 = cqs2.connect(url2, props)) {
+ //client-1 creates tables, views, indexes and view indexes
+ createTable(conn, baseTable, NEVER);
+ createView(conn, baseTable, view);
+ createIndex(conn, baseTable, index, "v2");
+ createIndex(conn, view, viewIndex, "v1");
+ createTable(conn, baseTable2, NEVER);
+ createView(conn, baseTable2, view2);
+ createIndex(conn, baseTable2, index2, "v2");
+ createIndex(conn, view2, viewIndex2, "v1");
+
+ //client-2 queries the view
+ query(conn2, view);
+
+ PTable basePTable = PhoenixRuntime.getTable(conn2, baseTable);
+ PTable viewPTable = PhoenixRuntime.getTable(conn2, view);
+ PTable viewIndexPTable = PhoenixRuntime.getTable(conn2,
SchemaUtil.getTableName(SCHEMA2, viewIndex));
+ PTable indexPTable = PhoenixRuntime.getTable(conn2,
SchemaUtil.getTableName(SCHEMA1, index));
+
+ //verify view has base table in ancestor map
+ Map<PTableKey,Long> map =
viewPTable.getAncestorLastDDLTimestampMap();
+ assertEquals(basePTable.getLastDDLTimestamp(),
map.get(basePTable.getKey()));
+
+ //verify view index has base table and view in ancestor map
+ map = viewIndexPTable.getAncestorLastDDLTimestampMap();
+ assertEquals(2, map.size());
+ assertEquals(basePTable.getLastDDLTimestamp(),
map.get(basePTable.getKey()));
+ assertEquals(viewPTable.getLastDDLTimestamp(),
map.get(viewPTable.getKey()));
+
+ //verify index has only base table in ancestor map
+ map = indexPTable.getAncestorLastDDLTimestampMap();
+ assertEquals(1, map.size());
+ assertEquals(basePTable.getLastDDLTimestamp(),
map.get(basePTable.getKey()));
+
+ //also verify index PTable within base table has the map
+ assertEquals(1, basePTable.getIndexes().size());
+ map =
basePTable.getIndexes().get(0).getAncestorLastDDLTimestampMap();
+ assertEquals(1, map.size());
+ assertEquals(basePTable.getLastDDLTimestamp(),
map.get(basePTable.getKey()));
+
+ //verify client-2 sees maps directly through PhoenixRuntime, no
query on baseTable2 or view2
+ PTable basePTable2 = PhoenixRuntime.getTable(conn2, baseTable2);
+ map = basePTable2.getAncestorLastDDLTimestampMap();
+ assertEquals(0, map.size());
+ assertEquals(1, basePTable2.getIndexes().size());
+ map =
basePTable2.getIndexes().get(0).getAncestorLastDDLTimestampMap();
+ assertEquals(basePTable2.getLastDDLTimestamp(),
map.get(basePTable2.getKey()));
+
+ PTable viewPTable2 = PhoenixRuntime.getTable(conn2, view2);
+ map = viewPTable2.getAncestorLastDDLTimestampMap();
+ assertEquals(basePTable2.getLastDDLTimestamp(),
map.get(basePTable2.getKey()));
+ assertEquals(2, viewPTable2.getIndexes().size());
+ for (PTable indexT : viewPTable2.getIndexes()) {
+ // inherited index
+ if (indexT.getTableName().getString().equals(index2)) {
+ map = indexT.getAncestorLastDDLTimestampMap();
+ assertEquals(basePTable2.getLastDDLTimestamp(),
map.get(basePTable2.getKey()));
+ } else {
+ // view index
+ map = indexT.getAncestorLastDDLTimestampMap();
+ assertEquals(basePTable2.getLastDDLTimestamp(),
map.get(basePTable2.getKey()));
+ assertEquals(viewPTable2.getLastDDLTimestamp(),
map.get(viewPTable2.getKey()));
+ }
+ }
+ }
+ }
+
+
//Helper methods
private long getLastDDLTimestamp(String tableName) throws SQLException {
Properties props = PropertiesUtil.deepCopy(TEST_PROPERTIES);