| /* |
| * 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 static org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_CATALOG_SCHEMA; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| import static org.apache.phoenix.query.BaseTest.generateUniqueName; |
| |
| import java.io.IOException; |
| import java.sql.Connection; |
| import java.sql.DriverManager; |
| import java.sql.ResultSet; |
| import java.sql.SQLException; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.TimeoutException; |
| |
| import org.apache.hadoop.conf.Configuration; |
| import org.apache.hadoop.hbase.HBaseTestingUtility; |
| import org.apache.hadoop.hbase.HConstants; |
| import org.apache.hadoop.hbase.HTableDescriptor; |
| import org.apache.hadoop.hbase.NamespaceNotFoundException; |
| import org.apache.hadoop.hbase.TableName; |
| import org.apache.phoenix.coprocessor.MetaDataProtocol; |
| import org.apache.phoenix.exception.SQLExceptionCode; |
| import org.apache.phoenix.exception.UpgradeRequiredException; |
| import org.apache.phoenix.jdbc.PhoenixConnection; |
| import org.apache.phoenix.jdbc.PhoenixDriver; |
| import org.apache.phoenix.jdbc.PhoenixEmbeddedDriver; |
| import org.apache.phoenix.jdbc.PhoenixTestDriver; |
| import org.apache.phoenix.query.ConnectionQueryServices; |
| import org.apache.phoenix.query.ConnectionQueryServicesImpl; |
| import org.apache.phoenix.query.QueryConstants; |
| import org.apache.phoenix.query.QueryServices; |
| import org.apache.phoenix.query.QueryServicesTestImpl; |
| import org.apache.phoenix.util.ReadOnlyProps; |
| import org.apache.phoenix.util.UpgradeUtil; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| @Category(NeedsOwnMiniClusterTest.class) |
| public class SystemCatalogCreationOnConnectionIT { |
| private HBaseTestingUtility testUtil = null; |
| private Set<String> hbaseTables; |
| private static boolean setOldTimestampToInduceUpgrade = false; |
| private static int countUpgradeAttempts; |
| // This flag is used to figure out if the SYSCAT schema was actually upgraded or not, based on the timestamp of SYSCAT |
| // (different from an upgrade attempt) |
| private static int actualSysCatUpgrades; |
| private static final String PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG = "SYSTEM:CATALOG"; |
| private static final String PHOENIX_SYSTEM_CATALOG = "SYSTEM.CATALOG"; |
| private static final String EXECUTE_UPGRADE_COMMAND = "EXECUTE UPGRADE"; |
| private static final String MODIFIED_MAX_VERSIONS ="5"; |
| private static final String CREATE_TABLE_STMT = "CREATE TABLE %s" |
| + " (k1 VARCHAR NOT NULL, k2 VARCHAR, CONSTRAINT PK PRIMARY KEY(K1,K2))"; |
| private static final String SELECT_STMT = "SELECT * FROM %s"; |
| private static final String DELETE_STMT = "DELETE FROM %s"; |
| private static final String CREATE_INDEX_STMT = "CREATE INDEX DUMMY_IDX ON %s (K1) INCLUDE (K2)"; |
| private static final String UPSERT_STMT = "UPSERT INTO %s VALUES ('A', 'B')"; |
| |
| private static final Set<String> PHOENIX_SYSTEM_TABLES = new HashSet<>(Arrays.asList( |
| "SYSTEM.CATALOG", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "SYSTEM.FUNCTION", |
| "SYSTEM.MUTEX", "SYSTEM.LOG", "SYSTEM.CHILD_LINK", "SYSTEM.TASK")); |
| |
| private static final Set<String> PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES = new HashSet<>( |
| Arrays.asList("SYSTEM:CATALOG", "SYSTEM:SEQUENCE", "SYSTEM:STATS", "SYSTEM:FUNCTION", |
| "SYSTEM:MUTEX", "SYSTEM:LOG", "SYSTEM:CHILD_LINK", "SYSTEM:TASK")); |
| |
| private static class PhoenixSysCatCreationServices extends ConnectionQueryServicesImpl { |
| |
| PhoenixSysCatCreationServices(QueryServices services, PhoenixEmbeddedDriver.ConnectionInfo connectionInfo, Properties info) { |
| super(services, connectionInfo, info); |
| } |
| |
| @Override |
| protected void setUpgradeRequired() { |
| super.setUpgradeRequired(); |
| countUpgradeAttempts++; |
| } |
| |
| @Override |
| protected long getSystemTableVersion() { |
| if (setOldTimestampToInduceUpgrade) { |
| // Return the next lower version where an upgrade was performed to induce setting the upgradeRequired flag |
| return MetaDataProtocol.getPriorUpgradeVersion(); |
| } |
| return MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP; |
| } |
| |
| @Override |
| protected PhoenixConnection upgradeSystemCatalogIfRequired(PhoenixConnection metaConnection, |
| long currentServerSideTableTimeStamp) throws InterruptedException, SQLException, TimeoutException, IOException { |
| PhoenixConnection newMetaConnection = super.upgradeSystemCatalogIfRequired(metaConnection, currentServerSideTableTimeStamp); |
| if (currentServerSideTableTimeStamp < MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP) { |
| actualSysCatUpgrades++; |
| } |
| return newMetaConnection; |
| } |
| } |
| |
| public static class PhoenixSysCatCreationTestingDriver extends PhoenixTestDriver { |
| private ConnectionQueryServices cqs; |
| private final ReadOnlyProps overrideProps; |
| |
| PhoenixSysCatCreationTestingDriver(ReadOnlyProps props) { |
| overrideProps = props; |
| } |
| |
| @Override // public for testing |
| public synchronized ConnectionQueryServices getConnectionQueryServices(String url, Properties info) throws SQLException { |
| if (cqs == null) { |
| cqs = new PhoenixSysCatCreationServices(new QueryServicesTestImpl(getDefaultProps(), overrideProps), ConnectionInfo.create(url), info); |
| cqs.init(url, info); |
| } |
| return cqs; |
| } |
| |
| // NOTE: Do not use this if you want to try re-establishing a connection from the client using a previously |
| // used ConnectionQueryServices instance. This is used only in cases where we need to test server-side |
| // changes and don't care about client-side properties set from the init method. |
| // Reset the Connection Query Services instance so we can create a new connection to the cluster |
| void resetCQS() { |
| cqs = null; |
| } |
| } |
| |
| |
| @Before |
| public void resetVariables() { |
| setOldTimestampToInduceUpgrade = false; |
| countUpgradeAttempts = 0; |
| actualSysCatUpgrades = 0; |
| } |
| |
| @After |
| public void tearDownMiniCluster() { |
| try { |
| if (testUtil != null) { |
| testUtil.shutdownMiniCluster(); |
| testUtil = null; |
| } |
| } catch (Exception e) { |
| // ignore |
| } |
| } |
| |
| |
| // Conditions: isDoNotUpgradePropSet is true |
| // Expected: We do not create SYSTEM.CATALOG even if this is the first connection to the server |
| @Test |
| public void testFirstConnectionDoNotUpgradePropSet() throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); |
| Properties propsDoNotUpgradePropSet = new Properties(); |
| // Set doNotUpgradeProperty to true |
| UpgradeUtil.doNotUpgradeOnFirstConnection(propsDoNotUpgradePropSet); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| |
| driver.getConnectionQueryServices(getJdbcUrl(), propsDoNotUpgradePropSet); |
| hbaseTables = getHBaseTables(); |
| assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); |
| assertEquals(0, hbaseTables.size()); |
| assertEquals(1, countUpgradeAttempts); |
| } |
| |
| |
| /********************* Testing SYSTEM.CATALOG/SYSTEM:CATALOG creation/upgrade behavior for subsequent connections *********************/ |
| |
| |
| // Conditions: server-side namespace mapping is enabled, the first connection to the server will create all namespace |
| // mapped SYSTEM tables i.e. SYSTEM:.*, the SYSTEM:CATALOG timestamp at creation is purposefully set to be < |
| // MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP. The subsequent connection has client-side namespace mapping enabled |
| // Expected: An upgrade is attempted when the second client connects to the server |
| @Test |
| public void testUpgradeAttempted() throws Exception { |
| setOldTimestampToInduceUpgrade = true; |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerEnabledClientEnabled(); |
| driver.resetCQS(); |
| Properties clientProps = getClientProperties(true, true); |
| setOldTimestampToInduceUpgrade = false; |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| // There should be no new tables |
| assertEquals(hbaseTables, getHBaseTables()); |
| // Since we set an old timestamp on purpose when creating SYSTEM:CATALOG, the second connection attempts to upgrade it |
| assertEquals(1, countUpgradeAttempts); |
| assertEquals(1, actualSysCatUpgrades); |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, the first connection to the server will create all namespace |
| // mapped SYSTEM tables i.e. SYSTEM:.*, the SYSTEM:CATALOG timestamp at creation is purposefully set to be < |
| // MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP. The subsequent connection has client-side namespace mapping enabled |
| // Expected: An upgrade is attempted when the second client connects to the server, but this fails since the |
| // isDoNotUpgradePropSet is set to true. We later run EXECUTE UPGRADE manually |
| @Test |
| public void testUpgradeNotAllowed() throws Exception { |
| setOldTimestampToInduceUpgrade = true; |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerEnabledClientEnabled(); |
| driver.resetCQS(); |
| Properties clientProps = getClientProperties(true, true); |
| UpgradeUtil.doNotUpgradeOnFirstConnection(clientProps); |
| setOldTimestampToInduceUpgrade = false; |
| try { |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| } catch (Exception e) { |
| assertTrue(e instanceof UpgradeRequiredException); |
| } |
| // There should be no new tables |
| assertEquals(hbaseTables, getHBaseTables()); |
| // Since we set an old timestamp on purpose when creating SYSTEM:CATALOG, the second connection attempts to upgrade it |
| assertEquals(1, countUpgradeAttempts); |
| // This connection is unable to actually upgrade SYSTEM:CATALOG due to isDoNotUpgradePropSet |
| assertEquals(0, actualSysCatUpgrades); |
| Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), new Properties()).connect(getJdbcUrl(), new Properties()); |
| try { |
| conn.createStatement().execute(EXECUTE_UPGRADE_COMMAND); |
| // Actually upgraded SYSTEM:CATALOG |
| assertEquals(1, actualSysCatUpgrades); |
| } finally { |
| conn.close(); |
| } |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, the first connection to the server will create unmapped SYSTEM |
| // tables SYSTEM\..* whose timestamp at creation is purposefully set to be < MetaDataProtocol.MIN_SYSTEM_TABLE_TIMESTAMP. |
| // The second connection has client-side namespace mapping enabled and system table to system namespace mapping enabled |
| // Expected: We will migrate all SYSTEM\..* tables to the SYSTEM namespace and also upgrade SYSTEM:CATALOG |
| @Test |
| public void testMigrateToSystemNamespaceAndUpgradeSysCat() throws Exception { |
| setOldTimestampToInduceUpgrade = true; |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerEnabledClientEnabledMappingDisabled(); |
| driver.resetCQS(); |
| setOldTimestampToInduceUpgrade = false; |
| Properties clientProps = getClientProperties(true, true); |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); |
| assertEquals(1, countUpgradeAttempts); |
| assertEquals(1, actualSysCatUpgrades); |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, the first connection to the server will create all namespace |
| // mapped SYSTEM tables i.e. SYSTEM:.*, the second connection has client-side namespace mapping disabled |
| // Expected: Throw Inconsistent namespace mapping exception from ensureTableCreated |
| @Test |
| public void testTablesExistInconsistentNSMappingFails() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerEnabledClientEnabled(); |
| driver.resetCQS(); |
| Properties clientProps = getClientProperties(false, false); |
| try { |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); |
| } catch (SQLException sqlE) { |
| assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); |
| } |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, the first connection to the server will not create any |
| // SYSTEM tables. The second connection has client-side namespace mapping enabled |
| // Expected: We create SYSTEM:.* tables |
| @Test |
| public void testIncompatibleNSMappingServerEnabledConnectionFails() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerEnabledClientDisabled(); |
| driver.resetCQS(); |
| // now try a client with ns mapping enabled |
| Properties clientProps = getClientProperties(true, true); |
| Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), clientProps) |
| .connect(getJdbcUrl(), new Properties()); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| |
| ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM SYSTEM.CATALOG LIMIT 1"); |
| // Tests that SYSTEM:CATALOG contains necessary metadata rows for itself (See PHOENIX-5302) |
| assertTrue(rs.next()); |
| } |
| |
| // Conditions: server-side namespace mapping is disabled, the first connection to the server will create all unmapped |
| // SYSTEM tables i.e. SYSTEM\..*, the second connection has client-side namespace mapping enabled |
| // Expected: Throw Inconsistent namespace mapping exception when you check client-server compatibility |
| // |
| // Then another connection has client-side namespace mapping disabled |
| // Expected: All SYSTEM\..* tables exist and no upgrade is required |
| @Test |
| public void testSysTablesExistNSMappingDisabled() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerDisabledClientDisabled(); |
| driver.resetCQS(); |
| Properties clientProps = getClientProperties(true, true); |
| try { |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); |
| } catch (SQLException sqlE) { |
| assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); |
| } |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| |
| driver.resetCQS(); |
| clientProps = getClientProperties(false, false); |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| } |
| |
| // Conditions: server-side namespace mapping is disabled, the first connection to the server will not create any |
| // SYSTEM tables. The second connection has client-side namespace mapping disabled |
| // Expected: The second connection should create all SYSTEM.* tables |
| @Test |
| public void testIncompatibleNSMappingServerDisabledConnectionFails() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| firstConnectionNSMappingServerDisabledClientEnabled(); |
| driver.resetCQS(); |
| // now try a client with ns mapping disabled |
| Properties clientProps = getClientProperties(false, false); |
| Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), clientProps) |
| .connect(getJdbcUrl(), new Properties()); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| |
| ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM SYSTEM.CATALOG LIMIT 1"); |
| // Tests that SYSTEM.CATALOG contains necessary metadata rows for itself (See PHOENIX-5302) |
| assertTrue(rs.next()); |
| } |
| |
| // Conditions: The first connection creates all SYSTEM tables via "EXECUTE UPGRADE" since auto-upgrade is disabled |
| // and the same client alters HBase metadata for SYSTEM.CATALOG |
| // Expected: Another client connection (with a new ConnectionQueryServices instance) made to the server does not |
| // revert the metadata change |
| @Test |
| public void testMetadataAlterRemainsAutoUpgradeDisabled() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = firstConnectionAutoUpgradeToggle(false); |
| assertEquals(Integer.parseInt(MODIFIED_MAX_VERSIONS), verifyModificationTableMetadata(driver, PHOENIX_SYSTEM_CATALOG)); |
| } |
| |
| // Conditions: The first connection creates all SYSTEM tables (auto-upgrade is enabled) and the same client alters |
| // HBase metadata for SYSTEM.CATALOG |
| // Expected: Another client connection (with a new ConnectionQueryServices instance) made to the server does not |
| // revert the metadata change |
| @Test |
| public void testMetadataAlterRemainsAutoUpgradeEnabled() throws Exception { |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = firstConnectionAutoUpgradeToggle(true); |
| assertEquals(Integer.parseInt(MODIFIED_MAX_VERSIONS), verifyModificationTableMetadata(driver, PHOENIX_SYSTEM_CATALOG)); |
| } |
| |
| // Test the case when an end-user uses the vanilla PhoenixDriver to create a connection and a |
| // requirement for upgrade is detected. In this case, the user should get a connection on which |
| // they are only able to run "EXECUTE UPGRADE" |
| @Test |
| public void testExecuteUpgradeSameConnWithPhoenixDriver() throws Exception { |
| // Register the vanilla PhoenixDriver |
| DriverManager.registerDriver(PhoenixDriver.INSTANCE); |
| startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); |
| Properties propsDoNotUpgradePropSet = new Properties(); |
| // Set doNotUpgradeProperty to true |
| UpgradeUtil.doNotUpgradeOnFirstConnection(propsDoNotUpgradePropSet); |
| |
| Connection conn = DriverManager.getConnection(getJdbcUrl(), propsDoNotUpgradePropSet); |
| hbaseTables = getHBaseTables(); |
| assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) |
| || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); |
| assertEquals(0, hbaseTables.size()); |
| |
| // Test that we are unable to run any other queries using this connection until we upgrade |
| final String tableName = generateUniqueName(); |
| try { |
| conn.createStatement().execute(String.format(CREATE_TABLE_STMT, tableName)); |
| fail("CREATE TABLE should have failed with UpgradeRequiredException"); |
| } catch (UpgradeRequiredException expected) { |
| |
| } |
| try { |
| conn.createStatement().execute(String.format(SELECT_STMT, tableName)); |
| fail("SELECT should have failed with UpgradeRequiredException"); |
| } catch (UpgradeRequiredException expected) { |
| |
| } |
| try { |
| conn.createStatement().execute(String.format(DELETE_STMT, tableName)); |
| fail("DELETE should have failed with UpgradeRequiredException"); |
| } catch (UpgradeRequiredException expected) { |
| |
| } |
| try { |
| conn.createStatement().execute(String.format(CREATE_INDEX_STMT, tableName)); |
| fail("CREATE INDEX should have failed with UpgradeRequiredException"); |
| } catch (UpgradeRequiredException expected) { |
| |
| } |
| try { |
| conn.createStatement().execute(String.format(UPSERT_STMT, tableName)); |
| fail("UPSERT VALUES should have failed with UpgradeRequiredException"); |
| } catch (UpgradeRequiredException expected) { |
| |
| } |
| |
| // Now run the upgrade command. All SYSTEM tables should be created |
| conn.createStatement().execute("EXECUTE UPGRADE"); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| |
| // Now we can run any other query/mutation using this connection object |
| conn.createStatement().execute(String.format(CREATE_TABLE_STMT, tableName)); |
| conn.createStatement().execute(String.format(SELECT_STMT, tableName)); |
| conn.createStatement().execute(String.format(DELETE_STMT, tableName)); |
| conn.createStatement().execute(String.format(CREATE_INDEX_STMT, tableName)); |
| conn.createStatement().execute(String.format(UPSERT_STMT, tableName)); |
| } |
| |
| /** |
| * Return all created HBase tables |
| * @return Set of HBase table name strings |
| * @throws IOException |
| */ |
| private Set<String> getHBaseTables() throws IOException { |
| Set<String> tables = new HashSet<>(); |
| for (TableName tn : testUtil.getHBaseAdmin().listTableNames()) { |
| tables.add(tn.getNameAsString()); |
| } |
| return tables; |
| } |
| |
| // Check if the SYSTEM namespace has been created |
| private boolean isSystemNamespaceCreated() throws IOException { |
| try { |
| testUtil.getHBaseAdmin().getNamespaceDescriptor(SYSTEM_CATALOG_SCHEMA); |
| } catch (NamespaceNotFoundException ex) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Alter the table metadata and return modified value |
| * @param driver |
| * @param tableName |
| * @return value of VERSIONS option for the table |
| * @throws Exception |
| */ |
| private int verifyModificationTableMetadata(PhoenixSysCatCreationTestingDriver driver, String tableName) throws Exception { |
| // Modify table metadata |
| Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), new Properties()).connect(getJdbcUrl(), new Properties()); |
| conn.createStatement().execute("ALTER TABLE " + tableName + " SET VERSIONS = " + MODIFIED_MAX_VERSIONS); |
| |
| // Connect via a client that creates a new ConnectionQueryServices instance |
| driver.resetCQS(); |
| driver.getConnectionQueryServices(getJdbcUrl(), new Properties()).connect(getJdbcUrl(), new Properties()); |
| HTableDescriptor descriptor = testUtil.getHBaseAdmin().getTableDescriptor(TableName.valueOf(tableName)); |
| return descriptor.getFamily(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES).getMaxVersions(); |
| } |
| |
| /** |
| * Start the mini-cluster with server-side namespace mapping property specified |
| * @param isNamespaceMappingEnabled |
| * @throws Exception |
| */ |
| private void startMiniClusterWithToggleNamespaceMapping(String isNamespaceMappingEnabled) throws Exception { |
| testUtil = new HBaseTestingUtility(); |
| Configuration conf = testUtil.getConfiguration(); |
| conf.set(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, isNamespaceMappingEnabled); |
| // Avoid multiple clusters trying to bind to the master's info port (16010) |
| conf.setInt(HConstants.MASTER_INFO_PORT, -1); |
| testUtil.startMiniCluster(1); |
| } |
| |
| /** |
| * Get the connection string for the mini-cluster |
| * @return Phoenix connection string |
| */ |
| private String getJdbcUrl() { |
| return "jdbc:phoenix:localhost:" + testUtil.getZkCluster().getClientPort() + ":/hbase"; |
| } |
| |
| /** |
| * Set namespace mapping related properties for the client connection |
| * @param nsMappingEnabled |
| * @param systemTableMappingEnabled |
| * @return Properties object |
| */ |
| private Properties getClientProperties(boolean nsMappingEnabled, boolean systemTableMappingEnabled) { |
| Properties clientProps = new Properties(); |
| clientProps.setProperty(QueryServices.IS_NAMESPACE_MAPPING_ENABLED, Boolean.valueOf(nsMappingEnabled).toString()); |
| clientProps.setProperty(QueryServices.IS_SYSTEM_TABLE_MAPPED_TO_NAMESPACE, Boolean.valueOf(systemTableMappingEnabled).toString()); |
| return clientProps; |
| } |
| |
| /** |
| * Initiate the first connection to the server with provided auto-upgrade property |
| * @param isAutoUpgradeEnabled |
| * @return Phoenix JDBC driver |
| * @throws Exception |
| */ |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionAutoUpgradeToggle(boolean isAutoUpgradeEnabled) |
| throws Exception { |
| if (isAutoUpgradeEnabled) { |
| return firstConnectionNSMappingServerDisabledClientDisabled(); |
| } |
| return firstConnectionAutoUpgradeDisabled(); |
| } |
| |
| // Conditions: isAutoUpgradeEnabled is false |
| // Expected: We do not create SYSTEM.CATALOG even if this is the first connection to the server. Later, when we manually |
| // run "EXECUTE UPGRADE", we create SYSTEM tables |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionAutoUpgradeDisabled() throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); |
| Map<String, String> props = new HashMap<>(); |
| // Note that the isAutoUpgradeEnabled property is set when instantiating connection query services, not during init |
| props.put(QueryServices.AUTO_UPGRADE_ENABLED, Boolean.FALSE.toString()); |
| ReadOnlyProps readOnlyProps = new ReadOnlyProps(props); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(readOnlyProps); |
| |
| // We should be able to get a connection, however upgradeRequired should be set so that we |
| // are not allowed to run any query/mutation until "EXECUTE UPGRADE" has been run |
| Connection conn = driver.getConnectionQueryServices(getJdbcUrl(), new Properties()) |
| .connect(getJdbcUrl(), new Properties()); |
| hbaseTables = getHBaseTables(); |
| assertFalse(hbaseTables.contains(PHOENIX_SYSTEM_CATALOG) || hbaseTables.contains(PHOENIX_NAMESPACE_MAPPED_SYSTEM_CATALOG)); |
| assertEquals(0, hbaseTables.size()); |
| assertEquals(1, countUpgradeAttempts); |
| |
| // We use the same connection to run "EXECUTE UPGRADE" |
| try { |
| conn.createStatement().execute(EXECUTE_UPGRADE_COMMAND); |
| } finally { |
| conn.close(); |
| } |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| return driver; |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, client-side namespace mapping is enabled and system tables |
| // are to be mapped to the SYSTEM namespace. |
| // Expected: If this is the first connection to the server, we should be able to create all namespace mapped system tables i.e. SYSTEM:.* |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerEnabledClientEnabled() |
| throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.TRUE.toString()); |
| Properties clientProps = getClientProperties(true, true); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_NAMESPACE_MAPPED_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| assertTrue(isSystemNamespaceCreated()); |
| return driver; |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, client-side namespace mapping is enabled, but mapping |
| // SYSTEM tables to the SYSTEM namespace is disabled |
| // Expected: If this is the first connection to the server, we will create unmapped SYSTEM tables i.e. SYSTEM\..* |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerEnabledClientEnabledMappingDisabled() |
| throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.TRUE.toString()); |
| // client-side namespace mapping is enabled, but mapping SYSTEM tables to SYSTEM namespace is not |
| Properties clientProps = getClientProperties(true, false); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| assertFalse(isSystemNamespaceCreated()); |
| return driver; |
| } |
| |
| // Conditions: server-side namespace mapping is enabled, client-side namespace mapping is disabled |
| // Expected: Since this is the first connection to the server, we will immediately |
| // throw an exception for inconsistent namespace mapping without creating any SYSTEM tables |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerEnabledClientDisabled() |
| throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.TRUE.toString()); |
| Properties clientProps = getClientProperties(false, false); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| try { |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); |
| } catch (SQLException sqlE) { |
| assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); |
| } |
| hbaseTables = getHBaseTables(); |
| assertEquals(0, hbaseTables.size()); |
| assertEquals(0, countUpgradeAttempts); |
| return driver; |
| } |
| |
| // Conditions: server-side namespace mapping is disabled, client-side namespace mapping is enabled |
| // Expected: Since this is the first connection to the server, we will immediately throw an exception for |
| // inconsistent namespace mapping without creating any SYSTEM tables or SYSTEM namespace |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerDisabledClientEnabled() |
| throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); |
| Properties clientProps = getClientProperties(true, true); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| try { |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| fail("Client should not be able to connect to cluster with inconsistent client-server namespace mapping properties"); |
| } catch (SQLException sqlE) { |
| assertEquals(SQLExceptionCode.INCONSISTENT_NAMESPACE_MAPPING_PROPERTIES.getErrorCode(), sqlE.getErrorCode()); |
| } |
| hbaseTables = getHBaseTables(); |
| assertEquals(0, hbaseTables.size()); |
| assertEquals(0, countUpgradeAttempts); |
| assertFalse(isSystemNamespaceCreated()); |
| return driver; |
| } |
| |
| // Conditions: server-side namespace mapping is disabled, client-side namespace mapping is disabled |
| // Expected: Since this is the first connection to the server and auto-upgrade is enabled by default, |
| // we will create all SYSTEM\..* tables |
| private SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver firstConnectionNSMappingServerDisabledClientDisabled() |
| throws Exception { |
| startMiniClusterWithToggleNamespaceMapping(Boolean.FALSE.toString()); |
| Properties clientProps = getClientProperties(false, false); |
| SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver driver = |
| new SystemCatalogCreationOnConnectionIT.PhoenixSysCatCreationTestingDriver(ReadOnlyProps.EMPTY_PROPS); |
| driver.getConnectionQueryServices(getJdbcUrl(), clientProps); |
| hbaseTables = getHBaseTables(); |
| assertEquals(PHOENIX_SYSTEM_TABLES, hbaseTables); |
| assertEquals(0, countUpgradeAttempts); |
| assertFalse(isSystemNamespaceCreated()); |
| return driver; |
| } |
| } |