This is an automated email from the ASF dual-hosted git repository. vjasani pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/master by this push: new 62ccb3b815 PHOENIX-7067 View indexes should be created only on non overlapping updatable views (#1709) 62ccb3b815 is described below commit 62ccb3b815aca1c1cd2f2b9fb8feab024bec834f Author: Viraj Jasani <vjas...@apache.org> AuthorDate: Thu Nov 9 18:13:07 2023 -0900 PHOENIX-7067 View indexes should be created only on non overlapping updatable views (#1709) --- .../org/apache/phoenix/end2end/AlterTableIT.java | 75 +- .../phoenix/end2end/MetaDataEndpointImplIT.java | 26 +- .../end2end/ViewExtendsPkRestrictionsIT.java | 1169 ++++++++++++++++++++ .../java/org/apache/phoenix/end2end/ViewTTLIT.java | 65 +- .../phoenix/compile/CreateTableCompiler.java | 61 + .../apache/phoenix/exception/SQLExceptionCode.java | 4 + .../phoenix/jdbc/PhoenixDatabaseMetaData.java | 2 + .../query/DelegateConnectionQueryServices.java | 2 +- .../phoenix/query/DelegateQueryServices.java | 4 +- .../org/apache/phoenix/query/QueryServices.java | 11 + .../org/apache/phoenix/schema/MetaDataClient.java | 74 +- 11 files changed, 1420 insertions(+), 73 deletions(-) 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 b6c1cc1b12..8908af2047 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 @@ -1182,50 +1182,93 @@ public class AlterTableIT extends ParallelStatsDisabledIT { } @Test - public void testDeclaringColumnAsRowTimestamp() throws Exception { + public void testIndexColumnAsRowTimestamp() throws Exception { try (Connection conn = DriverManager.getConnection(getUrl())) { - conn.createStatement().execute("CREATE TABLE " + dataTableFullName + " (PK1 DATE NOT NULL, PK2 VARCHAR NOT NULL, KV1 VARCHAR CONSTRAINT PK PRIMARY KEY(PK1 ROW_TIMESTAMP, PK2)) " + tableDDLOptions); - PhoenixConnection phxConn = conn.unwrap(PhoenixConnection.class); - PTable table = phxConn.getTable(new PTableKey(phxConn.getTenantId(), dataTableFullName)); + conn.createStatement().execute("CREATE TABLE " + dataTableFullName + + " (PK1 DATE NOT NULL, PK2 VARCHAR NOT NULL, KV1 VARCHAR " + + "CONSTRAINT PK PRIMARY KEY(PK1 ROW_TIMESTAMP, PK2)) " + tableDDLOptions); + PhoenixConnection phxConn = conn.unwrap(PhoenixConnection.class); + PTable table = + phxConn.getTable(new PTableKey(phxConn.getTenantId(), dataTableFullName)); // Assert that the column shows up as row time stamp in the cache. assertTrue(table.getColumnForColumnName("PK1").isRowTimestamp()); assertFalse(table.getColumnForColumnName("PK2").isRowTimestamp()); assertIsRowTimestampSet(schemaName, dataTableName, "PK1"); - + String dataTableName2 = BaseTest.generateUniqueName(); String dataTableFullName2 = SchemaUtil.getTableName(schemaName, dataTableName2); - conn.createStatement().execute("CREATE IMMUTABLE TABLE " + dataTableFullName2 + " (PK1 VARCHAR, PK2 DATE PRIMARY KEY ROW_TIMESTAMP, KV1 VARCHAR, KV2 INTEGER)"); + conn.createStatement().execute("CREATE IMMUTABLE TABLE " + dataTableFullName2 + + " (PK1 VARCHAR, PK2 DATE PRIMARY KEY ROW_TIMESTAMP, KV1 VARCHAR, KV2 INTEGER)"); table = phxConn.getTable(new PTableKey(phxConn.getTenantId(), dataTableFullName2)); // Assert that the column shows up as row time stamp in the cache. assertFalse(table.getColumnForColumnName("PK1").isRowTimestamp()); assertTrue(table.getColumnForColumnName("PK2").isRowTimestamp()); assertIsRowTimestampSet(schemaName, dataTableName2, "PK2"); - + // Create an index on a table has a row time stamp pk column. The column should show up as a row time stamp column for the index too. - conn.createStatement().execute("CREATE INDEX " + indexTableName + " ON " + dataTableFullName2 + " (KV1) include (KV2)"); - PTable indexTable = phxConn.getTable(new PTableKey(phxConn.getTenantId(), indexTableFullName)); + conn.createStatement().execute( + "CREATE INDEX " + indexTableName + " ON " + dataTableFullName2 + + " (KV1) include (KV2)"); + PTable indexTable = + phxConn.getTable(new PTableKey(phxConn.getTenantId(), indexTableFullName)); String indexColName = IndexUtil.getIndexColumnName(table.getColumnForColumnName("PK2")); // Assert that the column shows up as row time stamp in the cache. assertTrue(indexTable.getColumnForColumnName(indexColName).isRowTimestamp()); assertIsRowTimestampSet(schemaName, indexTableName, indexColName); + } + } + + @Test + public void testDeclaringColumnAsRowTimestamp() throws Exception { + try (Connection conn = DriverManager.getConnection(getUrl())) { + conn.createStatement().execute("CREATE TABLE " + dataTableFullName + + " (PK1 DATE NOT NULL, PK2 VARCHAR NOT NULL, KV1 VARCHAR " + + "CONSTRAINT PK PRIMARY KEY(PK1 ROW_TIMESTAMP, PK2)) " + tableDDLOptions); + PhoenixConnection phxConn = conn.unwrap(PhoenixConnection.class); + PTable table = + phxConn.getTable(new PTableKey(phxConn.getTenantId(), dataTableFullName)); + // Assert that the column shows up as row time stamp in the cache. + assertTrue(table.getColumnForColumnName("PK1").isRowTimestamp()); + assertFalse(table.getColumnForColumnName("PK2").isRowTimestamp()); + assertIsRowTimestampSet(schemaName, dataTableName, "PK1"); + + String dataTableName2 = BaseTest.generateUniqueName(); + String dataTableFullName2 = SchemaUtil.getTableName(schemaName, dataTableName2); + conn.createStatement().execute("CREATE IMMUTABLE TABLE " + dataTableFullName2 + + " (PK1 VARCHAR, PK2 DATE PRIMARY KEY ROW_TIMESTAMP, KV1 VARCHAR, KV2 INTEGER)"); + table = phxConn.getTable(new PTableKey(phxConn.getTenantId(), dataTableFullName2)); + // Assert that the column shows up as row time stamp in the cache. + assertFalse(table.getColumnForColumnName("PK1").isRowTimestamp()); + assertTrue(table.getColumnForColumnName("PK2").isRowTimestamp()); + assertIsRowTimestampSet(schemaName, dataTableName2, "PK2"); + String viewTableName2 = dataTableName2 + "_VIEW"; String viewTableFullName2 = SchemaUtil.getTableName(schemaName, viewTableName2); // Creating a view with a row_timestamp column in its pk constraint is not allowed try { - conn.createStatement().execute("CREATE VIEW " + viewTableFullName2 + " (KV3 VARCHAR, KV4 DATE, KV5 INTEGER, CONSTRAINT PK PRIMARY KEY (KV3, KV4 ROW_TIMESTAMP) ) AS SELECT * FROM " + dataTableFullName2); - fail("Creating a view with a row_timestamp column in its pk constraint is not allowed"); + conn.createStatement().execute("CREATE VIEW " + viewTableFullName2 + + " (KV3 VARCHAR, KV4 DATE, KV5 INTEGER, " + + "CONSTRAINT PK PRIMARY KEY (KV3, KV4 ROW_TIMESTAMP) ) AS SELECT * FROM " + + dataTableFullName2); + fail("Creating a view with a row_timestamp column in its pk constraint is not " + + "allowed"); } catch (SQLException e) { - assertEquals(SQLExceptionCode.ROWTIMESTAMP_NOT_ALLOWED_ON_VIEW.getErrorCode(), e.getErrorCode()); + assertEquals(SQLExceptionCode.ROWTIMESTAMP_NOT_ALLOWED_ON_VIEW.getErrorCode(), + e.getErrorCode()); } - + // Make sure that the base table column declared as row_timestamp is also row_timestamp for view - conn.createStatement().execute("CREATE VIEW " + viewTableFullName2 + " (KV3 VARCHAR, KV4 VARCHAR, KV5 INTEGER, CONSTRAINT PK PRIMARY KEY (KV3, KV4) ) AS SELECT * FROM " + dataTableFullName2); - PTable view = phxConn.getTable(new PTableKey(phxConn.getTenantId(), viewTableFullName2)); + conn.createStatement().execute("CREATE VIEW " + viewTableFullName2 + + " (KV3 VARCHAR, KV4 VARCHAR, KV5 INTEGER, " + + "CONSTRAINT PK PRIMARY KEY (KV3, KV4)) AS SELECT * FROM " + + dataTableFullName2); + PTable view = + phxConn.getTable(new PTableKey(phxConn.getTenantId(), viewTableFullName2)); assertNotNull(view.getPKColumn("PK2")); assertTrue(view.getPKColumn("PK2").isRowTimestamp()); } } - + private void assertIsRowTimestampSet(String schemaName, String tableName, String columnName) throws SQLException { String sql = "SELECT IS_ROW_TIMESTAMP FROM \"SYSTEM\".\"CATALOG\" WHERE " + "(TABLE_SCHEM, TABLE_NAME) = ('" + schemaName + "','"+ tableName + "') AND\n" diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/MetaDataEndpointImplIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MetaDataEndpointImplIT.java index e30a6e0236..7f0c9d9e25 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/MetaDataEndpointImplIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/MetaDataEndpointImplIT.java @@ -101,7 +101,7 @@ public class MetaDataEndpointImplIT extends ParallelStatsDisabledIT { } @Test - public void testUpsertIntoChildViewWithPKAndIndex() throws Exception { + public void testUpsertIntoChildViewWithPK() throws Exception { String baseTable = generateUniqueName(); String view = generateUniqueName(); String childView = generateUniqueName(); @@ -117,12 +117,7 @@ public class MetaDataEndpointImplIT extends ParallelStatsDisabledIT { + "V4 VARCHAR CONSTRAINT PKVIEW PRIMARY KEY(V2, V3)) AS SELECT * FROM " + baseTable + " WHERE KEY_PREFIX = '0CY'"; conn.createStatement().execute(view1DDL); - - // Create an Index on the base view - String view1Index = generateUniqueName() + "_IDX"; - conn.createStatement().execute("CREATE INDEX " + view1Index + - " ON " + view + " (V2, V3) include (V1, V4)"); - + // Create a child view with primary key constraint String childViewDDL = "CREATE VIEW IF NOT EXISTS " + childView + " (V5 VARCHAR NOT NULL, V6 VARCHAR NOT NULL CONSTRAINT PK PRIMARY KEY " @@ -137,7 +132,7 @@ public class MetaDataEndpointImplIT extends ParallelStatsDisabledIT { } @Test - public void testUpsertIntoTenantChildViewWithPKAndIndex() throws Exception { + public void testUpsertIntoTenantChildViewWithPK() throws Exception { String baseTable = generateUniqueName(); String view = generateUniqueName(); String childView = generateUniqueName(); @@ -155,11 +150,6 @@ public class MetaDataEndpointImplIT extends ParallelStatsDisabledIT { + baseTable + " WHERE KEY_PREFIX = '0CY'"; conn.createStatement().execute(view1DDL); - // Create an Index on the base view - String view1Index = generateUniqueName() + "_IDX"; - conn.createStatement().execute("CREATE INDEX " + view1Index + - " ON " + view + " (V2, V3) include (V1, V4)"); - // Create a child view with primary key constraint owned by tenant Properties tenantProps = new Properties(); tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId); @@ -167,12 +157,12 @@ public class MetaDataEndpointImplIT extends ParallelStatsDisabledIT { String childViewDDL = "CREATE VIEW IF NOT EXISTS " + childView + " (V5 VARCHAR NOT NULL, V6 VARCHAR NOT NULL CONSTRAINT PK PRIMARY KEY " + "(V5, V6)) AS SELECT * FROM " + view; - conn.createStatement().execute(childViewDDL); + tenantConn.createStatement().execute(childViewDDL); + String upsert = "UPSERT INTO " + childView + " (TENANT_ID, V2, V3, V5, V6) " + + "VALUES ('00D005000000000', 'zzzzz', 10, 'zzzzz', 'zzzzz')"; + tenantConn.createStatement().executeUpdate(upsert); + tenantConn.commit(); } - - String upsert = "UPSERT INTO " + childView + " (TENANT_ID, V2, V3, V5, V6) " - + "VALUES ('00D005000000000', 'zzzzz', 10, 'zzzzz', 'zzzzz')"; - conn.createStatement().executeUpdate(upsert); conn.commit(); } } diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewExtendsPkRestrictionsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewExtendsPkRestrictionsIT.java new file mode 100644 index 0000000000..05071ecba7 --- /dev/null +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewExtendsPkRestrictionsIT.java @@ -0,0 +1,1169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.phoenix.end2end; + +import org.apache.phoenix.util.PhoenixRuntime; +import org.apache.phoenix.util.SchemaUtil; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + +import static org.apache.phoenix.exception.SQLExceptionCode.CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK; +import static org.apache.phoenix.exception.SQLExceptionCode.VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES; +import static org.apache.phoenix.query.QueryServices.DISABLE_VIEW_SUBTREE_VALIDATION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +/** + * Tests for restrictions associated with view extending primary key of its parent. + */ +@Category(ParallelStatsDisabledTest.class) +public class ViewExtendsPkRestrictionsIT extends ParallelStatsDisabledIT { + + private static final Logger LOGGER = LoggerFactory.getLogger(ViewExtendsPkRestrictionsIT.class); + + private static final String TENANT_ID = "tenant_01"; + + private Connection getTenantConnection(final String tenantId) throws Exception { + Properties tenantProps = new Properties(); + tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId); + return DriverManager.getConnection(getUrl(), tenantProps); + } + + private Connection getTenantConnection(final String tenantId, + final boolean disableCreateIndexCheck) throws Exception { + Properties tenantProps = new Properties(); + tenantProps.setProperty(PhoenixRuntime.TENANT_ID_ATTRIB, tenantId); + tenantProps.setProperty(DISABLE_VIEW_SUBTREE_VALIDATION, + Boolean.toString(disableCreateIndexCheck)); + return DriverManager.getConnection(getUrl(), tenantProps); + } + + @Test + public void testViewExtendsPkWithParentTableIndex1() { + final String tableName = generateUniqueName(); + final String indexName = "idx_" + tableName; + final String view01 = "v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute( + "CREATE INDEX " + indexName + " ON " + tableName + " (COL3) INCLUDE " + "(COL4)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL PRIMARY KEY, COL5 VARCHAR) AS SELECT * FROM " + + tableName + " WHERE COL1 = 'col1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testSchemaViewExtendsPkWithParentTableIndex1() { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String indexName = "idx_" + tableName; + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE INDEX " + indexName + " ON " + fullTableName + " (COL3) INCLUDE " + + "(COL4)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL PRIMARY KEY, COL5 VARCHAR) AS SELECT * FROM " + + fullTableName + " WHERE COL1 = 'col1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithParentTableIndex1() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String indexName = "idx_" + tableName; + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + stmt.execute("CREATE INDEX " + indexName + " ON " + fullTableName + " (COL3) INCLUDE " + + "(COL4)"); + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL PRIMARY KEY, COL5 VARCHAR) AS SELECT * FROM " + + fullTableName + " WHERE COL1 = 'col1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + } + } + + @Test + public void testViewExtendsPkWithParentTableIndex2() { + final String tableName = generateUniqueName(); + final String indexName = "idx_" + tableName; + final String view01 = "v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute( + "CREATE INDEX " + indexName + " ON " + tableName + " (COL3) INCLUDE " + "(COL4)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testViewExtendsPkWithViewIndex1() throws Exception { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR)" + " AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + } + } + + @Test + public void testViewExtendsPkWithViewIndex2() { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(VCOL2)) AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testSchemaViewExtendsPkWithViewIndex2() { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(VCOL2)) AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex2() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute( + "CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + tenantStmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(VCOL2)) AS SELECT * FROM " + view01 + + " WHERE VCOL1 = 'vcol1'"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testViewExtendsPkWithViewIndex3() { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String view03 = "v03_" + tableName; + final String view04 = "v04_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col2'"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + fail(); + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex3() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL2 CHAR(10), " + "COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex4() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String schemaName6 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String view05 = SchemaUtil.getTableName(schemaName6, "v05_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL4 CHAR(10), " + "COL8 VARCHAR) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + tenantStmt.execute("CREATE VIEW " + view05 + " (VCOL5 CHAR(10), " + + "COL9 VARCHAR, COL10 INTEGER CONSTRAINT pk PRIMARY KEY(VCOL5)) AS " + + "SELECT * FROM " + view04 + " WHERE VCOL4 = 'vcol4'"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex5() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String schemaName6 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String view05 = SchemaUtil.getTableName(schemaName6, "v05_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL4 CHAR(10), " + "COL8 VARCHAR) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + tenantStmt.execute("CREATE VIEW " + view05 + " (VCOL5 CHAR(10), " + + "COL9 VARCHAR, COL10 INTEGER) AS " + "SELECT * FROM " + view04 + + " WHERE VCOL4 = 'vcol4'"); + } + } + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex6() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String schemaName6 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String view05 = SchemaUtil.getTableName(schemaName6, "v05_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL4 CHAR(10), " + "COL8 VARCHAR) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + tenantStmt.execute( + "CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + allStmtExecuted = true; + tenantStmt.execute("CREATE VIEW " + view05 + " (VCOL5 CHAR(10), " + + "COL9 VARCHAR, COL10 INTEGER CONSTRAINT pk PRIMARY KEY(VCOL5)) AS " + + "SELECT * FROM " + view04 + " WHERE VCOL4 = 'vcol4'"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getErrorCode(), + e.getErrorCode()); + assertEquals(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex7() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String schemaName6 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String view05 = SchemaUtil.getTableName(schemaName6, "v05_" + tableName); + final String index_table = "idx_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL4 CHAR(10), " + "COL8 VARCHAR) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute( + "CREATE INDEX " + index_table + " ON " + fullTableName + " (COL4) INCLUDE " + + "(COL2)"); + tenantStmt.execute("CREATE VIEW " + view05 + " (VCOL5 CHAR(10), " + + "COL9 VARCHAR, COL10 INTEGER CONSTRAINT pk PRIMARY KEY(VCOL5)) AS " + + "SELECT * FROM " + view04 + " WHERE VCOL4 = 'vcol4'"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewExtendsPkWithViewIndex8() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_table = "idx_" + tableName; + + Properties props = new Properties(); + props.setProperty(DISABLE_VIEW_SUBTREE_VALIDATION, "true"); + + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID, true)) { + final Statement tenantStmt = tenantConn.createStatement(); + + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(8), COL6 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col2'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL4 CHAR(10), " + "COL8 VARCHAR) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + stmt.execute( + "CREATE INDEX " + index_table + " ON " + fullTableName + " (COL4) INCLUDE " + + "(COL2)"); + } + } + } + + @Test + public void testViewIndexWithChildViewExtendedPk1() throws Exception { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String view03 = "v03_" + tableName; + final String view04 = "v04_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + + Properties props = new Properties(); + props.setProperty(DISABLE_VIEW_SUBTREE_VALIDATION, "true"); + + try (Connection conn = DriverManager.getConnection(getUrl(), props)) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10) PRIMARY KEY, COL6 VARCHAR)" + + " AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + } + } + + @Test + public void testViewIndexWithChildViewExtendedPk4() { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String view03 = "v03_" + tableName; + final String view04 = "v04_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10) PRIMARY KEY, COL6 VARCHAR)" + + " AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testSchemaViewIndexWithChildViewExtendedPk1() { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10) PRIMARY KEY, COL6 VARCHAR)" + + " AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewIndexWithChildViewExtendedPk1() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + tenantStmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(10) PRIMARY KEY, COL6 VARCHAR)" + + " AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + tenantStmt.execute( + "CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view03 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + tenantStmt.execute( + "CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testViewIndexWithChildViewExtendedPk2() { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String view03 = "v03_" + tableName; + final String view04 = "v04_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(COL6)) AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testSchemaViewIndexWithChildViewExtendedPk2() { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + stmt.execute("CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(COL6)) AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute("CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view01 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testTenantSchemaViewIndexWithChildViewExtendedPk2() throws Exception { + final String tableName = generateUniqueName(); + final String schemaName1 = generateUniqueName(); + final String schemaName2 = generateUniqueName(); + final String schemaName3 = generateUniqueName(); + final String schemaName4 = generateUniqueName(); + final String schemaName5 = generateUniqueName(); + final String fullTableName = SchemaUtil.getTableName(schemaName1, tableName); + final String view01 = SchemaUtil.getTableName(schemaName2, "v01_" + tableName); + final String view02 = SchemaUtil.getTableName(schemaName3, "v02_" + tableName); + final String view03 = SchemaUtil.getTableName(schemaName4, "v03_" + tableName); + final String view04 = SchemaUtil.getTableName(schemaName5, "v04_" + tableName); + final String index_view01 = "idx_v01_" + tableName; + boolean allStmtExecuted = false; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + fullTableName + + " (TENANT_ID VARCHAR NOT NULL, COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT " + + "NULL, COL3 VARCHAR, COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(TENANT_ID, " + + "COL1, COL2)) MULTI_TENANT = true"); + + try (Connection tenantConn = getTenantConnection(TENANT_ID)) { + final Statement tenantStmt = tenantConn.createStatement(); + + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1))" + + " AS SELECT * FROM " + fullTableName + " WHERE COL1 = 'col1'"); + tenantStmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR CONSTRAINT pk " + + "PRIMARY KEY(VCOL2)) AS SELECT * FROM " + view01 + + " WHERE VCOL1 = 'vcol1'"); + tenantStmt.execute("CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + + "AS SELECT * FROM " + view01 + " WHERE VCOL1 = 'col3'"); + tenantStmt.execute( + "CREATE VIEW " + view04 + " (VCOL3 CHAR(10), COL6 VARCHAR PRIMARY KEY) AS " + + "SELECT * FROM " + view02 + " WHERE VCOL1 = 'vcol4'"); + allStmtExecuted = true; + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + fail(); + } + } catch (SQLException e) { + try { + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getErrorCode(), + e.getErrorCode()); + assertEquals(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getSQLState(), + e.getSQLState()); + assertTrue(e.getMessage() + .contains(CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK.getMessage())); + } catch (AssertionError ae) { + LOGGER.error("Exception: ", e); + throw ae; + } + } + assertTrue("All statements could not be executed", allStmtExecuted); + } + + @Test + public void testViewIndexWithChildViewExtendedPk3() throws Exception { + final String tableName = generateUniqueName(); + final String view01 = "v01_" + tableName; + final String view02 = "v02_" + tableName; + final String view03 = "v03_" + tableName; + final String view04 = "v04_" + tableName; + final String index_view01 = "idx_v01_" + tableName; + final String index_view02 = "idx_v02_" + tableName; + + try (Connection conn = DriverManager.getConnection(getUrl())) { + final Statement stmt = conn.createStatement(); + + stmt.execute("CREATE TABLE " + tableName + + " (COL1 CHAR(10) NOT NULL, COL2 CHAR(5) NOT NULL, COL3 VARCHAR," + + " COL4 VARCHAR CONSTRAINT pk PRIMARY KEY(COL1, COL2))"); + stmt.execute("CREATE VIEW " + view01 + + " (VCOL1 CHAR(8) NOT NULL, COL5 VARCHAR CONSTRAINT pk PRIMARY KEY(VCOL1)) " + + "AS SELECT * FROM " + tableName + " WHERE COL1 = 'col1'"); + stmt.execute( + "CREATE VIEW " + view02 + " (VCOL2 CHAR(10), COL6 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'vcol1'"); + stmt.execute("CREATE INDEX " + index_view02 + " ON " + view02 + " (COL6) INCLUDE " + + "(COL1, COL2, COL3)"); + stmt.execute( + "CREATE VIEW " + view03 + " (VCOL3 CHAR(8), COL7 VARCHAR) " + "AS SELECT * FROM " + + view01 + " WHERE VCOL1 = 'col3'"); + stmt.execute( + "CREATE VIEW " + view04 + " (VCOL2 CHAR(10), COL6 VARCHAR) AS " + "SELECT * FROM " + + view01 + " WHERE VCOL1 = 'vcol4'"); + stmt.execute("CREATE INDEX " + index_view01 + " ON " + view01 + " (COL5) INCLUDE " + + "(COL1, COL2, COL3)"); + } + } + +} diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewTTLIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewTTLIT.java index 77b3ec7ebd..7dba075b8b 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewTTLIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/ViewTTLIT.java @@ -211,8 +211,9 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { } private SchemaBuilder createLevel2TenantViewWithGlobalLevelTTL( - TenantViewOptions tenantViewOptions, TenantViewIndexOptions tenantViewIndexOptions) - throws Exception { + TenantViewOptions tenantViewOptions, + TenantViewIndexOptions tenantViewIndexOptions, + boolean allowIndex) throws Exception { // Define the test schema. // 1. Table with columns => (ORG_ID, KP, COL1, COL2, COL3), PK => (ORG_ID, KP) // 2. GlobalView with columns => (ID, COL4, COL5, COL6), PK => (ID) @@ -238,10 +239,20 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { if (tenantViewIndexOptions != null) { tenantViewIndexOverrideOptions = tenantViewIndexOptions; } - schemaBuilder.withTableOptions(tableOptions).withGlobalViewOptions(globalViewOptions) - .withGlobalViewIndexOptions(globalViewIndexOptions) - .withTenantViewOptions(tenantViewWithOverrideOptions) - .withTenantViewIndexOptions(tenantViewIndexOverrideOptions).buildWithNewTenant(); + if (allowIndex) { + schemaBuilder.withTableOptions(tableOptions) + .withGlobalViewOptions(globalViewOptions) + .withGlobalViewIndexOptions(globalViewIndexOptions) + .withTenantViewOptions(tenantViewWithOverrideOptions) + .withTenantViewIndexOptions(tenantViewIndexOverrideOptions) + .buildWithNewTenant(); + } else { + schemaBuilder.withTableOptions(tableOptions) + .withGlobalViewOptions(globalViewOptions) + .withTenantViewOptions(tenantViewWithOverrideOptions) + .withTenantViewIndexOptions(tenantViewIndexOverrideOptions) + .buildWithNewTenant(); + } return schemaBuilder; } @@ -259,10 +270,6 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { GlobalViewOptions globalViewOptions = GlobalViewOptions.withDefaults(); - SchemaBuilder.GlobalViewIndexOptions globalViewIndexOptions - = SchemaBuilder.GlobalViewIndexOptions.withDefaults(); - globalViewIndexOptions.setLocal(false); - TenantViewOptions tenantViewWithOverrideOptions = TenantViewOptions.withDefaults(); // Phoenix TTL is set to 300s => 300000 ms tenantViewWithOverrideOptions.setTableProps("PHOENIX_TTL=300"); @@ -274,7 +281,6 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { tenantViewIndexOverrideOptions = tenantViewIndexOptions; } schemaBuilder.withTableOptions(tableOptions).withGlobalViewOptions(globalViewOptions) - .withGlobalViewIndexOptions(globalViewIndexOptions) .withTenantViewOptions(tenantViewWithOverrideOptions) .withTenantViewIndexOptions(tenantViewIndexOverrideOptions).buildWithNewTenant(); return schemaBuilder; @@ -400,7 +406,8 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { public void testPhoenixTTLForLevelTwoView() throws Exception { long startTime = EnvironmentEdgeManager.currentTimeMillis(); - final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null); + final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null, + false); String tenantId = schemaBuilder.getDataOptions().getTenantId(); String schemaName = stripQuotes( @@ -409,25 +416,18 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { SchemaUtil.getTableNameFromFullName(schemaBuilder.getEntityGlobalViewName())); String tenantViewName = stripQuotes( SchemaUtil.getTableNameFromFullName(schemaBuilder.getEntityTenantViewName())); - String indexOnGlobalViewName = String.format("IDX_%s", globalViewName); - String indexOnTenantViewName = String - .format("IDX_%s", stripQuotes(schemaBuilder.getEntityKeyPrefix())); - // Expected 4 rows - one for GlobalView, one for TenantView and ViewIndex each. + // Expected 3 rows - one for GlobalView, one for TenantView and one for tenant view index. // Since the PHOENIX_TTL property values are being set, // we expect the view header columns to show up in regular scans too. assertViewHeaderRowsHavePhoenixTTLRelatedCells( - schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 4); + schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 3); assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, globalViewName, PTableType.VIEW.getSerializedValue(), 300000); - assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, indexOnGlobalViewName, - PTableType.INDEX.getSerializedValue(), 300000); // Since the PHOENIX_TTL property values are not being overridden, // we expect the TTL value to be same as the global view. assertSyscatHavePhoenixTTLRelatedColumns(tenantId, schemaName, tenantViewName, PTableType.VIEW.getSerializedValue(), 300000); - assertSyscatHavePhoenixTTLRelatedColumns(tenantId, schemaName, indexOnTenantViewName, - PTableType.INDEX.getSerializedValue(), 300000); } @Test @@ -515,7 +515,7 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { // Phoenix TTL is set to 120s => 120000 ms tenantViewWithOverrideOptions.setTableProps("PHOENIX_TTL=120"); final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL( - tenantViewWithOverrideOptions, null); + tenantViewWithOverrideOptions, null, false); fail(); } catch (SQLException e) { assertEquals(SQLExceptionCode.CANNOT_SET_OR_ALTER_PHOENIX_TTL.getErrorCode(), @@ -528,7 +528,8 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { long startTime = EnvironmentEdgeManager.currentTimeMillis(); // Phoenix TTL is set to 300s - final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null); + final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null, + false); String tenantId = schemaBuilder.getDataOptions().getTenantId(); String schemaName = stripQuotes( @@ -537,25 +538,18 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { SchemaUtil.getTableNameFromFullName(schemaBuilder.getEntityGlobalViewName())); String tenantViewName = stripQuotes( SchemaUtil.getTableNameFromFullName(schemaBuilder.getEntityTenantViewName())); - String indexOnGlobalViewName = String.format("IDX_%s", globalViewName); - String indexOnTenantViewName = String - .format("IDX_%s", stripQuotes(schemaBuilder.getEntityKeyPrefix())); - // Expected 4 rows - one for GlobalView, one for TenantView and ViewIndex each. + // Expected 3 rows - one for GlobalView, one for TenantView and tenant view index. // Since the PHOENIX_TTL property values are being set, // we expect the view header columns to show up in regular scans too. assertViewHeaderRowsHavePhoenixTTLRelatedCells( - schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 4); + schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 3); assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, globalViewName, PTableType.VIEW.getSerializedValue(), 300000); - assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, indexOnGlobalViewName, - PTableType.INDEX.getSerializedValue(), 300000); // Since the PHOENIX_TTL property values are not being overridden, // we expect the TTL value to be same as the global view. assertSyscatHavePhoenixTTLRelatedColumns(tenantId, schemaName, tenantViewName, PTableType.VIEW.getSerializedValue(), 300000); - assertSyscatHavePhoenixTTLRelatedColumns(tenantId, schemaName, indexOnTenantViewName, - PTableType.INDEX.getSerializedValue(), 300000); String tenantURL = getUrl() + ';' + TENANT_ID_ATTRIB + '=' + tenantId; try (Connection connection = DriverManager.getConnection(tenantURL)) { @@ -667,7 +661,8 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { public void testResetPhoenixTTL() throws Exception { long startTime = EnvironmentEdgeManager.currentTimeMillis(); - final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null); + final SchemaBuilder schemaBuilder = createLevel2TenantViewWithGlobalLevelTTL(null, null, + false); String tenantId = schemaBuilder.getDataOptions().getTenantId(); String schemaName = stripQuotes( SchemaUtil.getSchemaNameFromFullName(schemaBuilder.getEntityTenantViewName())); @@ -688,11 +683,11 @@ public class ViewTTLIT extends ParallelStatsDisabledIT { } } - // Expected 4 rows - one for GlobalView, one for TenantView and ViewIndex each. + // Expected 3 rows - one for GlobalView, one for TenantView and tenant view index. // Since the PHOENIX_TTL property values for global view are being reset, // we expect the view header columns value to be set to zero. assertViewHeaderRowsHavePhoenixTTLRelatedCells( - schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 4); + schemaBuilder.getTableOptions().getSchemaName(), startTime, false, 3); assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, globalViewName, PTableType.VIEW.getSerializedValue(), 0); assertSyscatHavePhoenixTTLRelatedColumns("", schemaName, indexOnGlobalViewName, diff --git a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java index 3986ea6607..5ca54662c4 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/compile/CreateTableCompiler.java @@ -63,12 +63,14 @@ import org.apache.phoenix.schema.PTable; import org.apache.phoenix.schema.PTable.ViewType; import org.apache.phoenix.schema.PTableType; import org.apache.phoenix.schema.SortOrder; +import org.apache.phoenix.schema.TableNotFoundException; import org.apache.phoenix.schema.TableRef; import org.apache.phoenix.schema.types.PDataType; import org.apache.phoenix.schema.types.PVarbinary; import org.apache.phoenix.thirdparty.com.google.common.collect.Iterators; import org.apache.phoenix.util.ByteUtil; import org.apache.phoenix.util.MetaDataUtil; +import org.apache.phoenix.util.PhoenixRuntime; import org.apache.phoenix.util.QueryUtil; import org.apache.phoenix.util.SchemaUtil; @@ -176,6 +178,7 @@ public class CreateTableCompiler { validateCreateViewCompilation(connection, parentToBe, columnDefs, pkConstraint); } + verifyIfAnyParentHasIndexesAndViewExtendsPk(parentToBe, columnDefs, pkConstraint); } final ViewType viewType = viewTypeToBe; final String viewStatement = viewStatementToBe; @@ -207,6 +210,46 @@ public class CreateTableCompiler { viewStatement, viewType, viewColumnConstants, isViewColumnReferenced, connection); } + /** + * If any of the parent table/view has indexes in the parent hierarchy, and the current + * view under creation extends the primary key of the parent, throw error. + * + * @param parentToBe parent table/view of the current view under creation. + * @param columnDefs list of column definitions. + * @param pkConstraint primary key constraint. + * @throws SQLException if the view extends primary key and one of the parent view/table has + * indexes in the parent hierarchy. + */ + private void verifyIfAnyParentHasIndexesAndViewExtendsPk(PTable parentToBe, + List<ColumnDef> columnDefs, + PrimaryKeyConstraint pkConstraint) + throws SQLException { + if (viewExtendsParentPk(columnDefs, pkConstraint)) { + PTable table = parentToBe; + while (table != null) { + if (table.getIndexes().size() > 0) { + throw new SQLExceptionInfo.Builder( + SQLExceptionCode + .VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES) + .build() + .buildException(); + } + if (table.getType() != PTableType.VIEW) { + return; + } + String schemaName = table.getParentSchemaName().getString(); + String tableName = table.getParentTableName().getString(); + try { + table = PhoenixRuntime.getTable( + statement.getConnection(), + SchemaUtil.getTableName(schemaName, tableName)); + } catch (TableNotFoundException e) { + table = null; + } + } + } + } + /** * Validate View creation compilation. * 1. If view creation syntax does not specify primary key, the method @@ -253,6 +296,24 @@ public class CreateTableCompiler { } } + /** + * Returns true if the view extends the primary key of the parent table/view, returns false + * otherwise. + * + * @param columnDefs column def list. + * @param pkConstraint primary key constraint. + * @return true if the view extends the primary key of the parent table/view, false otherwise. + */ + private boolean viewExtendsParentPk( + final List<ColumnDef> columnDefs, + final PrimaryKeyConstraint pkConstraint) { + if (pkConstraint.getColumnNames().size() > 0) { + return true; + } else { + return columnDefs.stream().anyMatch(ColumnDef::isPK); + } + } + public static class ColumnTrackingExpressionCompiler extends ExpressionCompiler { private final BitSet isColumnReferenced; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java index 3f4d5168af..5796562566 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/exception/SQLExceptionCode.java @@ -354,6 +354,10 @@ public enum SQLExceptionCode { + PhoenixDatabaseMetaData.PHOENIX_TTL + " property on an view when parent/child view has PHOENIX_TTL set,"), CHANGE_DETECTION_SUPPORTED_FOR_TABLES_AND_VIEWS_ONLY(10954, "44A36", CHANGE_DETECTION_ENABLED + " is only supported on tables and views"), + CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK(10955, "44A37", "Index can be created " + + "only if none of the child views extends primary key"), + VIEW_CANNOT_EXTEND_PK_WITH_PARENT_INDEXES(10956, "44A38", "View can extend parent primary key" + + " only if none of the parents have indexes in the parent hierarchy"), /** Sequence related */ SEQUENCE_ALREADY_EXIST(1200, "42Z00", "Sequence already exists.", new Factory() { diff --git a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java index 9bfaf070d8..ada35cc3e2 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/jdbc/PhoenixDatabaseMetaData.java @@ -423,6 +423,8 @@ public class PhoenixDatabaseMetaData implements DatabaseMetaData { public static final String SYSTEM_CHILD_LINK_TABLE = "CHILD_LINK"; public static final String SYSTEM_CHILD_LINK_NAME = SchemaUtil.getTableName(SYSTEM_CATALOG_SCHEMA, SYSTEM_CHILD_LINK_TABLE); public static final byte[] SYSTEM_CHILD_LINK_NAME_BYTES = Bytes.toBytes(SYSTEM_CHILD_LINK_NAME); + public static final byte[] SYSTEM_CHILD_LINK_NAMESPACE_BYTES = + SchemaUtil.getPhysicalTableName(SYSTEM_CHILD_LINK_NAME_BYTES, true).getName(); public static final TableName SYSTEM_LINK_HBASE_TABLE_NAME = TableName.valueOf(SYSTEM_CHILD_LINK_NAME); public static final String SYSTEM_TASK_TABLE = "TASK"; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java index 46edad7ce9..4934608bd6 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateConnectionQueryServices.java @@ -63,7 +63,7 @@ public class DelegateConnectionQueryServices extends DelegateQueryServices imple } @Override - protected ConnectionQueryServices getDelegate() { + public ConnectionQueryServices getDelegate() { return (ConnectionQueryServices)super.getDelegate(); } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java index e58aa5f5e5..ac241e0b29 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/DelegateQueryServices.java @@ -40,8 +40,8 @@ public class DelegateQueryServices implements QueryServices { public DelegateQueryServices(QueryServices queryServices) { parent = queryServices; } - - protected QueryServices getDelegate() { + + public QueryServices getDelegate() { return parent; } diff --git a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java index af2c518a1c..e477f28cf4 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/query/QueryServices.java @@ -435,6 +435,17 @@ public interface QueryServices extends SQLCloseable { */ String SERVER_MERGE_FOR_UNCOVERED_INDEX = "phoenix.query.global.server.merge.enable"; + /** + * Param to determine whether client can disable validation to figure out if any of the + * descendent views extend primary key of their parents. Since this is a bit of + * expensive call, we can opt in to disable it. By default, this check will always be performed + * while creating index (PHOENIX-7067) on any table or view. This config can be used for + * disabling other subtree validation purpose as well. + */ + String DISABLE_VIEW_SUBTREE_VALIDATION = "phoenix.disable.view.subtree.validation"; + + boolean DEFAULT_DISABLE_VIEW_SUBTREE_VALIDATION = false; + /** * Get executor service used for parallel scans */ 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 b4ce6c2351..fb33efbada 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 @@ -81,6 +81,8 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.STREAMING_TOPIC_NA import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYNC_INDEX_CREATED_DATE; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_TABLE; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CHILD_LINK_NAMESPACE_BYTES; +import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CHILD_LINK_NAME_BYTES; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_FUNCTION_TABLE; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_TASK_TABLE; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME; @@ -98,6 +100,8 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_INDEX_ID_DATA import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_STATEMENT; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.VIEW_TYPE; import static org.apache.phoenix.query.QueryConstants.SYSTEM_SCHEMA_NAME; +import static org.apache.phoenix.query.QueryServices.DEFAULT_DISABLE_VIEW_SUBTREE_VALIDATION; +import static org.apache.phoenix.query.QueryServices.DISABLE_VIEW_SUBTREE_VALIDATION; import static org.apache.phoenix.query.QueryServices.INDEX_CREATE_DEFAULT_STATE; import static org.apache.phoenix.thirdparty.com.google.common.collect.Sets.newLinkedHashSet; import static org.apache.phoenix.thirdparty.com.google.common.collect.Sets.newLinkedHashSetWithExpectedSize; @@ -153,6 +157,10 @@ import java.util.Set; import java.util.HashSet; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.client.Table; +import org.apache.phoenix.coprocessor.TableInfo; +import org.apache.phoenix.query.ConnectionlessQueryServicesImpl; +import org.apache.phoenix.query.DelegateQueryServices; import org.apache.phoenix.schema.task.SystemTaskParams; import org.apache.phoenix.thirdparty.com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.hbase.HConstants; @@ -1596,6 +1604,12 @@ public class MetaDataClient { } } + Configuration config = connection.getQueryServices().getConfiguration(); + if (!connection.getQueryServices().getProps() + .getBoolean(DISABLE_VIEW_SUBTREE_VALIDATION, + DEFAULT_DISABLE_VIEW_SUBTREE_VALIDATION)) { + verifyIfDescendentViewsExtendPk(dataTable, config); + } // for view indexes if (dataTable.getType() == PTableType.VIEW) { String physicalName = dataTable.getPhysicalName().getString(); @@ -1620,7 +1634,6 @@ public class MetaDataClient { } } - Configuration config = connection.getQueryServices().getConfiguration(); long threshold = Long.parseLong(config.get(QueryServices.CLIENT_INDEX_ASYNC_THRESHOLD)); if (threshold > 0 && !statement.isAsync()) { @@ -1701,6 +1714,65 @@ public class MetaDataClient { return buildIndex(table, tableRef); } + /** + * Go through all the descendent views from the child view hierarchy and find if any of the + * descendent views extends the primary key, throw error. + * + * @param tableOrView view or table on which the index is being created. + * @param config the configuration. + * @throws SQLException if any of the descendent views extends pk or if something goes wrong + * while querying descendent view hierarchy. + */ + private void verifyIfDescendentViewsExtendPk(PTable tableOrView, Configuration config) + throws SQLException { + if (connection.getQueryServices() instanceof ConnectionlessQueryServicesImpl) { + return; + } + if (connection.getQueryServices() instanceof DelegateQueryServices) { + DelegateQueryServices services = (DelegateQueryServices) connection.getQueryServices(); + if (services.getDelegate() instanceof ConnectionlessQueryServicesImpl) { + return; + } + } + byte[] systemChildLinkTable = SchemaUtil.isNamespaceMappingEnabled(null, config) ? + SYSTEM_CHILD_LINK_NAMESPACE_BYTES : + SYSTEM_CHILD_LINK_NAME_BYTES; + try (Table childLinkTable = + connection.getQueryServices().getTable(systemChildLinkTable)) { + byte[] tenantId = connection.getTenantId() == null ? null + : connection.getTenantId().getBytes(); + byte[] schemaNameBytes = tableOrView.getSchemaName().getBytes(); + byte[] viewOrTableName = tableOrView.getTableName().getBytes(); + Pair<List<PTable>, List<TableInfo>> descViews = + ViewUtil.findAllDescendantViews( + childLinkTable, + config, + tenantId, + schemaNameBytes, + viewOrTableName, + HConstants.LATEST_TIMESTAMP, + false); + List<PTable> legitimateChildViews = descViews.getFirst(); + int dataTableOrViewPkCols = tableOrView.getPKColumns().size(); + if (legitimateChildViews != null && legitimateChildViews.size() > 0) { + for (PTable childView : legitimateChildViews) { + if (childView.getPKColumns().size() > dataTableOrViewPkCols) { + LOGGER.error("Creation of view index not allowed as child view {}" + + " extends pk", childView.getName()); + throw new SQLExceptionInfo.Builder( + SQLExceptionCode + .CANNOT_CREATE_INDEX_CHILD_VIEWS_EXTEND_PK) + .build() + .buildException(); + } + } + } + } catch (IOException e) { + LOGGER.error("Error while retrieving descendent views", e); + throw new SQLException(e); + } + } + public MutationState dropSequence(DropSequenceStatement statement) throws SQLException { Long scn = connection.getSCN(); long timestamp = scn == null ? HConstants.LATEST_TIMESTAMP : scn;