This is an automated email from the ASF dual-hosted git repository. chinmayskulkarni pushed a commit to branch 4.x in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/4.x by this push: new 1d84495 PHOENIX-5958: Diverged view created via an older client still sees dropped column data 1d84495 is described below commit 1d844950bb4ec8221873ecd2b094c20f427cd984 Author: Chinmay Kulkarni <chinmayskulka...@gmail.com> AuthorDate: Wed Jul 29 23:48:47 2020 -0700 PHOENIX-5958: Diverged view created via an older client still sees dropped column data --- .../phoenix/end2end/BackwardCompatibilityIT.java | 120 ++++++++---- .../gold_query_create_diverged_view.txt} | 16 +- .../{query.sql => create_diverged_view.sql} | 10 +- .../{query_more.sql => query_add_data.sql} | 0 .../sql_files/{query.sql => query_create_add.sql} | 0 .../{query.sql => query_create_diverged_view.sql} | 10 +- .../phoenix/coprocessor/AddColumnMutator.java | 3 +- .../apache/phoenix/coprocessor/ColumnMutator.java | 3 +- .../phoenix/coprocessor/DropColumnMutator.java | 20 +- .../phoenix/coprocessor/MetaDataEndpointImpl.java | 4 +- .../org/apache/phoenix/schema/MetaDataClient.java | 8 +- .../java/org/apache/phoenix/schema/PTableImpl.java | 4 +- .../java/org/apache/phoenix/util/ViewUtil.java | 213 ++++++++++++--------- 13 files changed, 251 insertions(+), 160 deletions(-) diff --git a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BackwardCompatibilityIT.java b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BackwardCompatibilityIT.java index fd1adc9..fa614ce 100644 --- a/phoenix-core/src/it/java/org/apache/phoenix/end2end/BackwardCompatibilityIT.java +++ b/phoenix-core/src/it/java/org/apache/phoenix/end2end/BackwardCompatibilityIT.java @@ -21,6 +21,7 @@ import static org.apache.phoenix.query.BaseTest.setUpConfigForMiniCluster; import static org.apache.phoenix.util.TestUtil.TEST_PROPERTIES; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeFalse; @@ -83,20 +84,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; public class BackwardCompatibilityIT { private static final String SQL_DIR = "sql_files/"; - private static final String RESULT_DIR = "gold_files/"; + private static final String RESULTS_AND_GOLD_FILES_DIR = "gold_files/"; private static final String COMPATIBLE_CLIENTS_JSON = "compatible_client_versions.json"; private static final String BASH = "/bin/bash"; private static final String EXECUTE_QUERY_SH = "scripts/execute_query.sh"; + private static final String QUERY_PREFIX = "query_"; private static final String RESULT_PREFIX = "result_"; + private static final String GOLD_PREFIX = "gold_"; private static final String SQL_EXTENSION = ".sql"; private static final String TEXT_EXTENSION = ".txt"; private static final String CREATE_ADD = "create_add"; + private static final String CREATE_DIVERGED_VIEW = "create_diverged_view"; private static final String ADD_DATA = "add_data"; private static final String ADD_DELETE = "add_delete"; - private static final String QUERY = "query"; - private static final String QUERY_MORE = "query_more"; - private static final String QUERY_ADD_DELETE = "query_add_delete"; + private static final String QUERY_CREATE_ADD = QUERY_PREFIX + CREATE_ADD; + private static final String QUERY_ADD_DATA = QUERY_PREFIX + ADD_DATA; + private static final String QUERY_ADD_DELETE = QUERY_PREFIX + ADD_DELETE; + private static final String QUERY_CREATE_DIVERGED_VIEW = QUERY_PREFIX + CREATE_DIVERGED_VIEW; private static final String MVN_HOME = "maven.home"; private static final String JAVA_TMP_DIR = "java.io.tmpdir"; @@ -172,15 +177,58 @@ public class BackwardCompatibilityIT { public void testUpsertWithOldClient() throws Exception { // Insert data with old client and read with new client executeQueryWithClientVersion(compatibleClientVersion, CREATE_ADD); - executeQueriesWithCurrentVersion(QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueriesWithCurrentVersion(QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); + } + + @Test + public void testCreateDivergedViewWithOldClientReadFromNewClient() throws Exception { + // Create a base table, view and make it diverge from an old client + executeQueryWithClientVersion(compatibleClientVersion, CREATE_DIVERGED_VIEW); + executeQueriesWithCurrentVersion(QUERY_CREATE_DIVERGED_VIEW); + assertExpectedOutput(QUERY_CREATE_DIVERGED_VIEW); + } + + @Test + public void testCreateDivergedViewWithOldClientReadFromOldClient() throws Exception { + // Create a base table, view and make it diverge from an old client + executeQueryWithClientVersion(compatibleClientVersion, CREATE_DIVERGED_VIEW); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_DIVERGED_VIEW); + assertExpectedOutput(QUERY_CREATE_DIVERGED_VIEW); + } + + @Test + public void testCreateDivergedViewWithOldClientReadFromOldClientAfterUpgrade() + throws Exception { + // Create a base table, view and make it diverge from an old client + executeQueryWithClientVersion(compatibleClientVersion, CREATE_DIVERGED_VIEW); + try (Connection conn = DriverManager.getConnection(url)) { + // Just connect with a new client to cause a metadata upgrade + } + // Query with an old client again + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_DIVERGED_VIEW); + assertExpectedOutput(QUERY_CREATE_DIVERGED_VIEW); + } + + @Test + public void testCreateDivergedViewWithNewClientReadFromOldClient() throws Exception { + executeQueriesWithCurrentVersion(CREATE_DIVERGED_VIEW); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_DIVERGED_VIEW); + assertExpectedOutput(QUERY_CREATE_DIVERGED_VIEW); + } + + @Test + public void testCreateDivergedViewWithNewClientReadFromNewClient() throws Exception { + executeQueriesWithCurrentVersion(CREATE_DIVERGED_VIEW); + executeQueriesWithCurrentVersion(QUERY_CREATE_DIVERGED_VIEW); + assertExpectedOutput(QUERY_CREATE_DIVERGED_VIEW); } /** * Scenario: * 1. New Client connects to the updated server * 2. New Client creates tables and inserts data - * 3. Old Client reads the data inserted by the old client + * 3. Old Client reads the data inserted by the new client * * @throws Exception thrown if any errors encountered during query execution or file IO */ @@ -188,8 +236,8 @@ public class BackwardCompatibilityIT { public void testSelectWithOldClient() throws Exception { // Insert data with new client and read with old client executeQueriesWithCurrentVersion(CREATE_ADD); - executeQueryWithClientVersion(compatibleClientVersion, QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); } /** @@ -206,13 +254,13 @@ public class BackwardCompatibilityIT { public void testSelectUpsertWithNewClient() throws Exception { // Insert data with old client and read with new client executeQueryWithClientVersion(compatibleClientVersion, CREATE_ADD); - executeQueriesWithCurrentVersion(QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueriesWithCurrentVersion(QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); // Insert more data with new client and read with old client executeQueriesWithCurrentVersion(ADD_DATA); - executeQueryWithClientVersion(compatibleClientVersion, QUERY_MORE); - assertExpectedOutput(ADD_DATA, QUERY_MORE); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_ADD_DATA); + assertExpectedOutput(QUERY_ADD_DATA); } /** @@ -229,13 +277,13 @@ public class BackwardCompatibilityIT { public void testSelectUpsertWithOldClient() throws Exception { // Insert data with new client and read with old client executeQueriesWithCurrentVersion(CREATE_ADD); - executeQueryWithClientVersion(compatibleClientVersion, QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); // Insert more data with old client and read with new client executeQueryWithClientVersion(compatibleClientVersion, ADD_DATA); - executeQueriesWithCurrentVersion(QUERY_MORE); - assertExpectedOutput(ADD_DATA, QUERY_MORE); + executeQueriesWithCurrentVersion(QUERY_ADD_DATA); + assertExpectedOutput(QUERY_ADD_DATA); } /** @@ -251,13 +299,13 @@ public class BackwardCompatibilityIT { public void testUpsertDeleteWithOldClient() throws Exception { // Insert data with old client and read with new client executeQueryWithClientVersion(compatibleClientVersion, CREATE_ADD); - executeQueriesWithCurrentVersion(QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueriesWithCurrentVersion(QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); // Deletes with the old client executeQueryWithClientVersion(compatibleClientVersion, ADD_DELETE); executeQueryWithClientVersion(compatibleClientVersion, QUERY_ADD_DELETE); - assertExpectedOutput(ADD_DELETE, QUERY_ADD_DELETE); + assertExpectedOutput(QUERY_ADD_DELETE); } /** @@ -273,13 +321,13 @@ public class BackwardCompatibilityIT { public void testUpsertDeleteWithNewClient() throws Exception { // Insert data with old client and read with new client executeQueriesWithCurrentVersion(CREATE_ADD); - executeQueryWithClientVersion(compatibleClientVersion, QUERY); - assertExpectedOutput(CREATE_ADD, QUERY); + executeQueryWithClientVersion(compatibleClientVersion, QUERY_CREATE_ADD); + assertExpectedOutput(QUERY_CREATE_ADD); // Deletes with the new client executeQueriesWithCurrentVersion(ADD_DELETE); executeQueriesWithCurrentVersion(QUERY_ADD_DELETE); - assertExpectedOutput(ADD_DELETE, QUERY_ADD_DELETE); + assertExpectedOutput(QUERY_ADD_DELETE); } private void checkForPreConditions() throws Exception { @@ -312,10 +360,12 @@ public class BackwardCompatibilityIT { .getResource(SQL_DIR + operation + SQL_EXTENSION); assertNotNull(fileUrl); cmdParams.add(new File(fileUrl.getFile()).getAbsolutePath()); - fileUrl = BackwardCompatibilityIT.class.getClassLoader().getResource(RESULT_DIR); + fileUrl = BackwardCompatibilityIT.class.getClassLoader().getResource( + RESULTS_AND_GOLD_FILES_DIR); assertNotNull(fileUrl); - cmdParams.add(new File(fileUrl.getFile()).getAbsolutePath() + "/" + - RESULT_PREFIX + operation + TEXT_EXTENSION); + String resultFilePath = new File(fileUrl.getFile()).getAbsolutePath() + "/" + + RESULT_PREFIX + operation + TEXT_EXTENSION; + cmdParams.add(resultFilePath); cmdParams.add(System.getProperty(JAVA_TMP_DIR)); if (System.getProperty(MVN_HOME) != null) { @@ -342,7 +392,9 @@ public class BackwardCompatibilityIT { }; errorStreamThread.start(); p.waitFor(); - assertEquals(sb.toString(), 0, p.exitValue()); + assertEquals(String.format("Executing the query failed%s. Check the result file: %s", + sb.length() > 0 ? sb.append(" with : ").toString() : "", resultFilePath), + 0, p.exitValue()); } // Executes the SQL commands listed in the given operation file from the sql_files directory @@ -365,7 +417,8 @@ public class BackwardCompatibilityIT { ResultSet rs; String[] sqlCommands = sb.toString().split(";"); - URL fileUrl = BackwardCompatibilityIT.class.getClassLoader().getResource(RESULT_DIR); + URL fileUrl = BackwardCompatibilityIT.class.getClassLoader().getResource( + RESULTS_AND_GOLD_FILES_DIR); assertNotNull(fileUrl); final String resultFile = new File(fileUrl.getFile()).getAbsolutePath() + "/" + RESULT_PREFIX + operation + TEXT_EXTENSION; @@ -417,18 +470,18 @@ public class BackwardCompatibilityIT { // Compares the result file against the gold file to match for the expected output // for the given operation - private void assertExpectedOutput(String gold, String result) throws Exception { + private void assertExpectedOutput(String result) throws Exception { List<String> resultFile = Lists.newArrayList(); List<String> goldFile = Lists.newArrayList(); String line; try (BufferedReader resultFileReader = getBufferedReaderForResource( - RESULT_DIR + RESULT_PREFIX + result + TEXT_EXTENSION)) { + RESULTS_AND_GOLD_FILES_DIR + RESULT_PREFIX + result + TEXT_EXTENSION)) { while ((line = resultFileReader.readLine()) != null) { resultFile.add(line.trim()); } } try (BufferedReader goldFileReader = getBufferedReaderForResource( - RESULT_DIR + "gold_query_" + gold + TEXT_EXTENSION)) { + RESULTS_AND_GOLD_FILES_DIR + GOLD_PREFIX + result + TEXT_EXTENSION)) { while ((line = goldFileReader.readLine()) != null) { line = line.trim(); if ( !(line.isEmpty() || line.startsWith("*") || line.startsWith("/"))) { @@ -440,7 +493,8 @@ public class BackwardCompatibilityIT { // We take the first line in gold file and match against the result file to exclude any // other WARNING messages that comes as a result of the query execution int index = resultFile.indexOf(goldFile.get(0)); + assertNotEquals("Mismatch found between gold file and result file", -1, index); resultFile = resultFile.subList(index, resultFile.size()); - assertEquals(resultFile, goldFile); + assertEquals(goldFile, resultFile); } -} \ No newline at end of file +} diff --git a/phoenix-core/src/it/resources/sql_files/query.sql b/phoenix-core/src/it/resources/gold_files/gold_query_create_diverged_view.txt similarity index 79% copy from phoenix-core/src/it/resources/sql_files/query.sql copy to phoenix-core/src/it/resources/gold_files/gold_query_create_diverged_view.txt index ebf154a..dddaf5e 100644 --- a/phoenix-core/src/it/resources/sql_files/query.sql +++ b/phoenix-core/src/it/resources/gold_files/gold_query_create_diverged_view.txt @@ -14,11 +14,13 @@ * 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. - */ - -SELECT COUNT(*) from my_schema.my_table; -SELECT * FROM my_schema.my_table; -SELECT id from my_table_view; + */ -SELECT COUNT(*) from my_schema.my_table_immutable; -SELECT * FROM my_schema.my_table_immutable; +'COUNT(1)' +'1' +'COUNT(1)' +'1' +'A','B','C','D' +'2','200','def','-20' +'A','B','D','VA','VB' +'2','200','-20','91','101' diff --git a/phoenix-core/src/it/resources/sql_files/query.sql b/phoenix-core/src/it/resources/sql_files/create_diverged_view.sql similarity index 73% copy from phoenix-core/src/it/resources/sql_files/query.sql copy to phoenix-core/src/it/resources/sql_files/create_diverged_view.sql index ebf154a..59dee14 100644 --- a/phoenix-core/src/it/resources/sql_files/query.sql +++ b/phoenix-core/src/it/resources/sql_files/create_diverged_view.sql @@ -15,10 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -SELECT COUNT(*) from my_schema.my_table; -SELECT * FROM my_schema.my_table; -SELECT id from my_table_view; -SELECT COUNT(*) from my_schema.my_table_immutable; -SELECT * FROM my_schema.my_table_immutable; +CREATE TABLE IF NOT EXISTS S.T (A INTEGER PRIMARY KEY, B INTEGER, C VARCHAR, D INTEGER); +CREATE VIEW IF NOT EXISTS S.V (VA INTEGER, VB INTEGER) AS SELECT * FROM S.T WHERE B=200; +UPSERT INTO S.V (A, B, C, D, VA, VB) VALUES (2, 200, 'def', -20, 91, 101); +ALTER VIEW S.V DROP COLUMN C; diff --git a/phoenix-core/src/it/resources/sql_files/query_more.sql b/phoenix-core/src/it/resources/sql_files/query_add_data.sql similarity index 100% rename from phoenix-core/src/it/resources/sql_files/query_more.sql rename to phoenix-core/src/it/resources/sql_files/query_add_data.sql diff --git a/phoenix-core/src/it/resources/sql_files/query.sql b/phoenix-core/src/it/resources/sql_files/query_create_add.sql similarity index 100% copy from phoenix-core/src/it/resources/sql_files/query.sql copy to phoenix-core/src/it/resources/sql_files/query_create_add.sql diff --git a/phoenix-core/src/it/resources/sql_files/query.sql b/phoenix-core/src/it/resources/sql_files/query_create_diverged_view.sql similarity index 79% rename from phoenix-core/src/it/resources/sql_files/query.sql rename to phoenix-core/src/it/resources/sql_files/query_create_diverged_view.sql index ebf154a..e3d056c 100644 --- a/phoenix-core/src/it/resources/sql_files/query.sql +++ b/phoenix-core/src/it/resources/sql_files/query_create_diverged_view.sql @@ -15,10 +15,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -SELECT COUNT(*) from my_schema.my_table; -SELECT * FROM my_schema.my_table; -SELECT id from my_table_view; -SELECT COUNT(*) from my_schema.my_table_immutable; -SELECT * FROM my_schema.my_table_immutable; +SELECT COUNT(*) FROM S.T; +SELECT COUNT(*) FROM S.V; +SELECT * FROM S.T; +SELECT * FROM S.V; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/AddColumnMutator.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/AddColumnMutator.java index 71b4827..4504666 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/AddColumnMutator.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/AddColumnMutator.java @@ -292,7 +292,8 @@ public class AddColumnMutator implements ColumnMutator { Region region, List<ImmutableBytesPtr> invalidateList, List<Region.RowLock> locks, - long clientTimeStamp) { + long clientTimeStamp, + long clientVersion) { byte[] tenantId = rowKeyMetaData[TENANT_ID_INDEX]; byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX]; byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX]; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ColumnMutator.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ColumnMutator.java index aa090aa..9ef0515 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ColumnMutator.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/ColumnMutator.java @@ -51,7 +51,8 @@ public interface ColumnMutator { List<Mutation> tableMetadata, Region region, List<ImmutableBytesPtr> invalidateList, List<Region.RowLock> locks, - long clientTimeStamp) + long clientTimeStamp, + long clientVersion) throws IOException, SQLException; /** diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/DropColumnMutator.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/DropColumnMutator.java index 6422475..3213744 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/DropColumnMutator.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/DropColumnMutator.java @@ -67,6 +67,7 @@ import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TABLE_NAME_INDEX; import static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.TENANT_ID_INDEX; import static org.apache.phoenix.query.QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT; import static org.apache.phoenix.util.SchemaUtil.getVarChars; +import static org.apache.phoenix.util.ViewUtil.isViewDiverging; public class DropColumnMutator implements ColumnMutator { @@ -166,9 +167,8 @@ public class DropColumnMutator implements ColumnMutator { Region region, List<ImmutableBytesPtr> invalidateList, List<Region.RowLock> locks, - long clientTimeStamp) - throws SQLException { - + long clientTimeStamp, + long clientVersion) throws SQLException { byte[] tenantId = rowKeyMetaData[TENANT_ID_INDEX]; byte[] schemaName = rowKeyMetaData[SCHEMA_NAME_INDEX]; byte[] tableName = rowKeyMetaData[TABLE_NAME_INDEX]; @@ -215,7 +215,8 @@ public class DropColumnMutator implements ColumnMutator { deletePKColumn = columnToDelete.getFamilyName() == null; if (isView) { // if we are dropping a derived column add it to the excluded - // column list + // column list. Note that this is only done for 4.15+ clients + // since old clients do not have the isDerived field if (columnToDelete.isDerived()) { mutation = MetaDataUtil.cloneDeleteToPutAndAddColumn((Delete) mutation, TABLE_FAMILY_BYTES, LINK_TYPE_BYTES, @@ -224,8 +225,7 @@ public class DropColumnMutator implements ColumnMutator { iterator.set(mutation); } - if (table.getBaseColumnCount() != DIVERGED_VIEW_BASE_COLUMN_COUNT - && columnToDelete.isDerived()) { + if (isViewDiverging(columnToDelete, table, clientVersion)) { // If the column being dropped is inherited from the base table, // then the view is about to diverge itself from the base table. // The consequence of this divergence is that that any further @@ -254,12 +254,8 @@ public class DropColumnMutator implements ColumnMutator { columnToDelete); } // drop any indexes that need the column that is going to be dropped - tableAndDroppedColPairs.add(new Pair(table, columnToDelete)); - } catch (ColumnFamilyNotFoundException e) { - return new MetaDataProtocol.MetaDataMutationResult( - MetaDataProtocol.MutationCode.COLUMN_NOT_FOUND, - EnvironmentEdgeManager.currentTimeMillis(), table, columnToDelete); - } catch (ColumnNotFoundException e) { + tableAndDroppedColPairs.add(new Pair<>(table, columnToDelete)); + } catch (ColumnFamilyNotFoundException | ColumnNotFoundException e) { return new MetaDataProtocol.MetaDataMutationResult( MetaDataProtocol.MutationCode.COLUMN_NOT_FOUND, EnvironmentEdgeManager.currentTimeMillis(), table, columnToDelete); diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java index d960820..00ce50f 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java @@ -617,7 +617,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso && table.getViewType() != ViewType.MAPPED) { try (PhoenixConnection connection = QueryUtil.getConnectionOnServer(env.getConfiguration()).unwrap(PhoenixConnection.class)) { PTable pTable = PhoenixRuntime.getTableNoCache(connection, table.getParentName().getString()); - table = ViewUtil.addDerivedColumnsFromParent(connection, table, pTable); + table = ViewUtil.addDerivedColumnsFromParent(table, pTable); } } builder.setReturnCode(MetaDataProtos.MutationCode.TABLE_ALREADY_EXISTS); @@ -2717,7 +2717,7 @@ public class MetaDataEndpointImpl extends MetaDataProtocol implements Coprocesso getParentPhysicalTableName(table), table.getType()); result = mutator.validateAndAddMetadata(table, rowKeyMetaData, tableMetadata, region, - invalidateList, locks, clientTimeStamp); + invalidateList, locks, clientTimeStamp, clientVersion); // if the update mutation caused tables to be deleted, the mutation code returned // will be MutationCode.TABLE_ALREADY_EXISTS if (result != null 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 42ead50..1d6924b 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 @@ -20,6 +20,9 @@ package org.apache.phoenix.schema; import static com.google.common.collect.Sets.newLinkedHashSet; import static com.google.common.collect.Sets.newLinkedHashSetWithExpectedSize; import static org.apache.phoenix.coprocessor.BaseScannerRegionObserver.RUN_UPDATE_STATS_ASYNC_ATTRIB; +import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_MAJOR_VERSION; +import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_MINOR_VERSION; +import static org.apache.phoenix.coprocessor.MetaDataProtocol.PHOENIX_PATCH_NUMBER; import static org.apache.phoenix.coprocessor.tasks.IndexRebuildTask.INDEX_NAME; import static org.apache.phoenix.coprocessor.tasks.IndexRebuildTask.REBUILD_ALL; import static org.apache.phoenix.exception.SQLExceptionCode.INSUFFICIENT_MULTI_TENANT_COLUMNS; @@ -168,6 +171,7 @@ import org.apache.phoenix.coprocessor.MetaDataProtocol; import org.apache.phoenix.coprocessor.MetaDataProtocol.MetaDataMutationResult; import org.apache.phoenix.coprocessor.MetaDataProtocol.MutationCode; import org.apache.phoenix.coprocessor.MetaDataProtocol.SharedTableState; +import org.apache.phoenix.hbase.index.util.VersionUtil; import org.apache.phoenix.schema.stats.GuidePostsInfo; import org.apache.phoenix.util.ViewUtil; import org.apache.phoenix.util.JacksonUtil; @@ -903,8 +907,8 @@ public class MetaDataClient { if (!alwaysAddAncestorColumnsAndIndexes && !result.wasUpdated() && !parentResult.wasUpdated()) { return false; } - PTable resolvedTable = ViewUtil.addDerivedColumnsAndIndexesFromParent(connection, table, parentTable); - result.setTable(resolvedTable); + result.setTable(ViewUtil.addDerivedColumnsAndIndexesFromParent( + connection, table, parentTable)); return true; } return false; diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java index e073363..fcd09d3 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/PTableImpl.java @@ -811,7 +811,9 @@ public class PTableImpl implements PTable { // When cloning table, ignore the salt column as it will be added back in the constructor public static List<PColumn> getColumnsToClone(PTable table) { - return table.getBucketNum() == null ? table.getColumns() : table.getColumns().subList(1, table.getColumns().size()); + return table == null ? Collections.<PColumn> emptyList() : + (table.getBucketNum() == null ? table.getColumns() : + table.getColumns().subList(1, table.getColumns().size())); } /** diff --git a/phoenix-core/src/main/java/org/apache/phoenix/util/ViewUtil.java b/phoenix-core/src/main/java/org/apache/phoenix/util/ViewUtil.java index bcafd1f..174f6b0 100644 --- a/phoenix-core/src/main/java/org/apache/phoenix/util/ViewUtil.java +++ b/phoenix-core/src/main/java/org/apache/phoenix/util/ViewUtil.java @@ -282,6 +282,15 @@ public class ViewUtil { return view.getBaseColumnCount() == QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT; } + public static boolean isViewDiverging(PColumn columnToDelete, PTable view, + long clientVersion) { + // If we are dropping a column from a pre-4.15 client, the only way to know if the + // view is diverging is by comparing the base column count + return !isDivergedView(view) && (clientVersion < MIN_SPLITTABLE_SYSTEM_CATALOG ? + columnToDelete.getPosition() < view.getBaseColumnCount() : + columnToDelete.isDerived()); + } + /** * Adds indexes of the parent table to inheritedIndexes if the index contains all required columns */ @@ -370,16 +379,16 @@ public class ViewUtil { */ public static PTable addDerivedColumnsAndIndexesFromParent(PhoenixConnection connection, PTable table, PTable parentTable) throws SQLException { - PTable pTable = addDerivedColumnsFromParent(connection, table, parentTable); + PTable pTable = addDerivedColumnsFromParent(table, parentTable); boolean hasIndexId = table.getViewIndexId() != null; // For views : if (!hasIndexId) { // 1. need to resolve the views's own indexes so that any columns added by ancestors are included List<PTable> allIndexes = Lists.newArrayList(); - if (!pTable.getIndexes().isEmpty()) { + if (pTable !=null && pTable.getIndexes() !=null && !pTable.getIndexes().isEmpty()) { for (PTable viewIndex : pTable.getIndexes()) { - PTable resolvedViewIndex = - ViewUtil.addDerivedColumnsAndIndexesFromParent(connection, viewIndex, pTable); + PTable resolvedViewIndex = ViewUtil.addDerivedColumnsAndIndexesFromParent( + connection, viewIndex, pTable); if (resolvedViewIndex!=null) allIndexes.add(resolvedViewIndex); } @@ -398,25 +407,29 @@ public class ViewUtil { } /** - * Inherit all columns from the parent unless its an excluded column if the same columns is present in the parent - * and child (for table metadata created before PHOENIX-3534) we chose the child column over the parent column + * Inherit all columns from the parent unless it's an excluded column. + * If the same column is present in the parent and child (for table metadata created before + * PHOENIX-3534) we choose the child column over the parent column * @return table with inherited columns */ - public static PTable addDerivedColumnsFromParent(PhoenixConnection connection, - PTable table, PTable parentTable) throws SQLException { + public static PTable addDerivedColumnsFromParent(PTable view, PTable parentTable) + throws SQLException { // combine columns for view and view indexes - boolean hasIndexId = table.getViewIndexId() != null; - boolean isSalted = table.getBucketNum() != null; - boolean isDiverged = isDivergedView(table); + boolean hasIndexId = view.getViewIndexId() != null; + boolean isSalted = view.getBucketNum() != null; + boolean isDiverged = isDivergedView(view); + boolean isDivergedViewCreatedPre4_15 = isDiverged; List<PColumn> allColumns = Lists.newArrayList(); List<PColumn> excludedColumns = Lists.newArrayList(); // add my own columns first in reverse order - List<PColumn> myColumns = table.getColumns(); + List<PColumn> myColumns = view.getColumns(); // skip salted column as it will be created automatically myColumns = myColumns.subList(isSalted ? 1 : 0, myColumns.size()); for (int i = myColumns.size() - 1; i >= 0; i--) { PColumn pColumn = myColumns.get(i); if (pColumn.isExcluded()) { + // Diverged views created pre-4.15 will not have EXCLUDED_COLUMN linking rows + isDivergedViewCreatedPre4_15 = false; excludedColumns.add(pColumn); } allColumns.add(pColumn); @@ -426,13 +439,13 @@ public class ViewUtil { // then remove the data columns that have not been dropped, so that we get the columns that // have been dropped Map<PColumn, List<String>> indexRequiredDroppedDataColMap = - Maps.newHashMapWithExpectedSize(table.getColumns().size()); + Maps.newHashMapWithExpectedSize(view.getColumns().size()); if (hasIndexId) { - int indexPosOffset = (isSalted ? 1 : 0) + (table.isMultiTenant() ? 1 : 0) + 1; + int indexPosOffset = (isSalted ? 1 : 0) + (view.isMultiTenant() ? 1 : 0) + 1; ColumnNameTrackingExpressionCompiler expressionCompiler = new ColumnNameTrackingExpressionCompiler(); - for (int i = indexPosOffset; i < table.getPKColumns().size(); i++) { - PColumn indexColumn = table.getPKColumns().get(i); + for (int i = indexPosOffset; i < view.getPKColumns().size(); i++) { + PColumn indexColumn = view.getPKColumns().get(i); try { expressionCompiler.reset(); String expressionStr = IndexUtil.getIndexColumnExpressionStr(indexColumn); @@ -446,8 +459,8 @@ public class ViewUtil { } } - long maxTableTimestamp = table.getTimeStamp(); - int numPKCols = table.getPKColumns().size(); + long maxTableTimestamp = view.getTimeStamp(); + int numPKCols = view.getPKColumns().size(); // set the final table timestamp as the max timestamp of the view/view index or its ancestors maxTableTimestamp = Math.max(maxTableTimestamp, parentTable.getTimeStamp()); if (hasIndexId) { @@ -480,58 +493,10 @@ public class ViewUtil { entry.getValue().remove(dataColumnName); } } - } else { - List<PColumn> currAncestorTableCols = PTableImpl.getColumnsToClone(parentTable); - if (currAncestorTableCols != null) { - // add the ancestor columns in reverse order so that the final column list - // contains ancestor columns and then the view columns in the right order - for (int j = currAncestorTableCols.size() - 1; j >= 0; j--) { - PColumn column = currAncestorTableCols.get(j); - // for diverged views we always include pk columns of the base table. We - // have to include these pk columns to be able to support adding pk - // columns to the diverged view - // we only include regular columns that were created before the view - // diverged - if (isDiverged && column.getFamilyName() != null - && column.getTimestamp() > table.getTimeStamp()) { - continue; - } - // need to check if this column is in the list of excluded (dropped) - // columns of the view - int existingIndex = excludedColumns.indexOf(column); - if (existingIndex != -1) { - // if it is, only exclude the column if was created before the - // column was dropped in the view in order to handle the case where - // a base table column is dropped in a view, then dropped in the - // base table and then added back to the base table - if (column.getTimestamp() <= excludedColumns.get(existingIndex) - .getTimestamp()) { - continue; - } - } - if (column.isExcluded()) { - excludedColumns.add(column); - } else { - int existingColumnIndex = allColumns.indexOf(column); - if (existingColumnIndex != -1) { - // for diverged views if the view was created before - // PHOENIX-3534 the parent table columns will be present in the - // view PTable (since the base column count is - // QueryConstants.DIVERGED_VIEW_BASE_COLUMN_COUNT we can't - // filter them out) so we always pick the parent column - // for non diverged views if the same column exists in a parent - // and child, we keep the latest column - PColumn existingColumn = allColumns.get(existingColumnIndex); - if (isDiverged || column.getTimestamp() > existingColumn.getTimestamp()) { - allColumns.remove(existingColumnIndex); - allColumns.add(column); - } - } else { - allColumns.add(column); - } - } - } - } + } else if (!isDivergedViewCreatedPre4_15) { + // For diverged views created by a pre-4.15 client, we don't need to inherit columns + // from its ancestors + inheritColumnsFromParent(view, parentTable, isDiverged, excludedColumns, allColumns); } // at this point indexRequiredDroppedDataColMap only contain the columns required by a view // index that have dropped @@ -548,21 +513,13 @@ public class ViewUtil { } } } - // remove the excluded columns if the timestamp of the excludedColumn is newer - for (PColumn excludedColumn : excludedColumns) { - int index = allColumns.indexOf(excludedColumn); - if (index != -1) { - if (allColumns.get(index).getTimestamp() <= excludedColumn.getTimestamp()) { - allColumns.remove(excludedColumn); - } - } - } + List<PColumn> columnsToAdd = Lists.newArrayList(); int position = isSalted ? 1 : 0; // allColumns contains the columns in the reverse order for (int i = allColumns.size() - 1; i >= 0; i--) { PColumn column = allColumns.get(i); - if (table.getColumns().contains(column)) { + if (view.getColumns().contains(column)) { // for views this column is not derived from an ancestor columnsToAdd.add(new PColumnImpl(column, position++)); } else { @@ -576,12 +533,12 @@ public class ViewUtil { : columnsToAdd.size() - myColumns.size() + (isSalted ? 1 : 0); // Inherit view-modifiable properties from the parent table/view if the current view has // not previously modified this property - Long updateCacheFreq = (table.getType() != PTableType.VIEW || - table.hasViewModifiedUpdateCacheFrequency()) ? - table.getUpdateCacheFrequency() : parentTable.getUpdateCacheFrequency(); - Boolean useStatsForParallelization = (table.getType() != PTableType.VIEW || - table.hasViewModifiedUseStatsForParallelization()) ? - table.useStatsForParallelization() : parentTable.useStatsForParallelization(); + long updateCacheFreq = (view.getType() != PTableType.VIEW || + view.hasViewModifiedUpdateCacheFrequency()) ? + view.getUpdateCacheFrequency() : parentTable.getUpdateCacheFrequency(); + Boolean useStatsForParallelization = (view.getType() != PTableType.VIEW || + view.hasViewModifiedUseStatsForParallelization()) ? + view.useStatsForParallelization() : parentTable.useStatsForParallelization(); // When creating a PTable for views or view indexes, use the baseTable PTable for attributes // inherited from the physical base table. @@ -590,7 +547,7 @@ public class ViewUtil { // if a TableProperty is valid on a view and is mutable on a view, we use the value set // on the view if the view had previously modified the property, otherwise we propagate the // value from the base table (see PHOENIX-4763) - PTable pTable = PTableImpl.builderWithColumns(table, columnsToAdd) + PTable pTable = PTableImpl.builderWithColumns(view, columnsToAdd) .setImmutableRows(parentTable.isImmutableRows()) .setDisableWAL(parentTable.isWALDisabled()) .setMultiTenant(parentTable.isMultiTenant()) @@ -604,8 +561,7 @@ public class ViewUtil { PTable.QualifierEncodingScheme.NON_ENCODED_QUALIFIERS : parentTable.getEncodingScheme()) .setBaseColumnCount(baseTableColumnCount) .setTimeStamp(maxTableTimestamp) - .setExcludedColumns(excludedColumns == null ? - ImmutableList.<PColumn>of() : ImmutableList.copyOf(excludedColumns)) + .setExcludedColumns(ImmutableList.copyOf(excludedColumns)) .setUpdateCacheFrequency(updateCacheFreq) .setUseStatsForParallelization(useStatsForParallelization) .build(); @@ -615,6 +571,85 @@ public class ViewUtil { } /** + * Inherit all columns from the parent unless it's an excluded column. + * If the same column is present in the parent and child + * (for table metadata created before PHOENIX-3534 or when + * {@link org.apache.phoenix.query.QueryServices#ALLOW_SPLITTABLE_SYSTEM_CATALOG_ROLLBACK} is + * enabled) we choose the child column over the parent column. + * Note that we don't need to call this method for views created before 4.15 since they + * already contain all the columns from their ancestors. + * @param view PTable of the view + * @param parentTable PTable of the view's parent + * @param isDiverged true if it is a diverged view + * @param excludedColumns list of excluded columns + * @param allColumns list of all columns. Initially this contains just the columns in the view. + * We will populate inherited columns by adding them to this list + */ + static void inheritColumnsFromParent(PTable view, PTable parentTable, + boolean isDiverged, List<PColumn> excludedColumns, List<PColumn> allColumns) { + List<PColumn> currAncestorTableCols = PTableImpl.getColumnsToClone(parentTable); + if (currAncestorTableCols != null) { + // add the ancestor columns in reverse order so that the final column list + // contains ancestor columns and then the view columns in the right order + for (int j = currAncestorTableCols.size() - 1; j >= 0; j--) { + PColumn ancestorColumn = currAncestorTableCols.get(j); + // for diverged views we always include pk columns of the base table. We + // have to include these pk columns to be able to support adding pk + // columns to the diverged view. + // We only include regular columns that were created before the view + // diverged. + if (isDiverged && ancestorColumn.getFamilyName() != null + && ancestorColumn.getTimestamp() > view.getTimeStamp()) { + // If this is a diverged view, the ancestor column is not a PK and the ancestor + // column was added after the view diverged, ignore this ancestor column. + continue; + } + // need to check if this ancestor column is in the list of excluded (dropped) + // columns of the view + int existingExcludedIndex = excludedColumns.indexOf(ancestorColumn); + if (existingExcludedIndex != -1) { + // if it is, only exclude the ancestor column if it was created before the + // column was dropped in the view in order to handle the case where + // a base table column is dropped in a view, then dropped in the + // base table and then added back to the base table + if (ancestorColumn.getTimestamp() <= excludedColumns.get(existingExcludedIndex) + .getTimestamp()) { + continue; + } + } + // A diverged view from a pre-4.15 client won't ever go in this case since + // isExcluded was introduced in 4.15. If this is a 4.15+ client, excluded columns + // will be identifiable via PColumn#isExcluded() + if (ancestorColumn.isExcluded()) { + excludedColumns.add(ancestorColumn); + } else { + int existingColumnIndex = allColumns.indexOf(ancestorColumn); + if (existingColumnIndex != -1) { + // For non-diverged views, if the same column exists in a parent and child, + // we keep the latest column. + PColumn existingColumn = allColumns.get(existingColumnIndex); + if (!isDiverged && ancestorColumn.getTimestamp() > existingColumn.getTimestamp()) { + allColumns.remove(existingColumnIndex); + allColumns.add(ancestorColumn); + } + } else { + allColumns.add(ancestorColumn); + } + } + } + } + // remove the excluded columns if the timestamp of the excludedColumn is newer + for (PColumn excludedColumn : excludedColumns) { + int index = allColumns.indexOf(excludedColumn); + if (index != -1) { + if (allColumns.get(index).getTimestamp() <= excludedColumn.getTimestamp()) { + allColumns.remove(excludedColumn); + } + } + } + } + + /** * See PHOENIX-4763. If we are modifying any table-level properties that are mutable on a view, * we mark these cells in SYSTEM.CATALOG with tags to indicate that this view property should * not be kept in-sync with the base table and so we shouldn't propagate the base table's