From d627c8b1816ee542a7a36fd4a8508da11720bd72 Mon Sep 17 00:00:00 2001 From: Toshihiro Suzuki Date: Mon, 10 Nov 2025 09:50:10 +0000 Subject: [PATCH 1/2] Empty commit [skip ci] From 4e61f00c2850b88cbf0770a3b1a11b0bd1076303 Mon Sep 17 00:00:00 2001 From: Toshihiro Suzuki Date: Mon, 10 Nov 2025 18:49:53 +0900 Subject: [PATCH 2/2] Refactor JdbcAdmin (#3121) --- ...rTableIntegrationTestWithJdbcDatabase.java | 22 + .../jdbc/JdbcAdminImportTestUtils.java | 2 +- .../db/storage/jdbc/JdbcAdminTestUtils.java | 11 +- .../MultiStorageAdminTestUtils.java | 20 +- .../common/CommonDistributedStorageAdmin.java | 2 +- .../com/scalar/db/storage/jdbc/JdbcAdmin.java | 730 +-- .../scalar/db/storage/jdbc/RdbEngineDb2.java | 65 +- .../db/storage/jdbc/RdbEngineMysql.java | 38 +- .../db/storage/jdbc/RdbEngineOracle.java | 45 +- .../db/storage/jdbc/RdbEnginePostgresql.java | 37 +- .../db/storage/jdbc/RdbEngineSqlServer.java | 35 +- .../db/storage/jdbc/RdbEngineSqlite.java | 42 +- .../db/storage/jdbc/RdbEngineStrategy.java | 22 +- .../db/storage/jdbc/TableMetadataService.java | 420 ++ ...cAdminTestBase.java => JdbcAdminTest.java} | 4420 ++++++++--------- .../jdbc/JdbcAdminWithDefaultConfigTest.java | 10 - ...dminWithTableMetadataSchemaConfigTest.java | 11 - .../jdbc/JdbcOperationCheckerTest.java | 2 - .../db/storage/jdbc/RdbEngineOracleTest.java | 6 +- .../storage/jdbc/RdbEnginePostgresqlTest.java | 6 +- 20 files changed, 2993 insertions(+), 2953 deletions(-) create mode 100644 core/src/main/java/com/scalar/db/storage/jdbc/TableMetadataService.java rename core/src/test/java/com/scalar/db/storage/jdbc/{JdbcAdminTestBase.java => JdbcAdminTest.java} (70%) delete mode 100644 core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithDefaultConfigTest.java delete mode 100644 core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithTableMetadataSchemaConfigTest.java diff --git a/core/src/integration-test/java/com/scalar/db/storage/jdbc/ConsensusCommitAdminRepairTableIntegrationTestWithJdbcDatabase.java b/core/src/integration-test/java/com/scalar/db/storage/jdbc/ConsensusCommitAdminRepairTableIntegrationTestWithJdbcDatabase.java index e30222f52b..a925f14eb3 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/jdbc/ConsensusCommitAdminRepairTableIntegrationTestWithJdbcDatabase.java +++ b/core/src/integration-test/java/com/scalar/db/storage/jdbc/ConsensusCommitAdminRepairTableIntegrationTestWithJdbcDatabase.java @@ -1,19 +1,41 @@ package com.scalar.db.storage.jdbc; +import com.google.common.util.concurrent.Uninterruptibles; +import com.google.errorprone.annotations.concurrent.LazyInit; +import com.scalar.db.config.DatabaseConfig; import com.scalar.db.transaction.consensuscommit.ConsensusCommitAdminRepairTableIntegrationTestBase; import com.scalar.db.util.AdminTestUtils; import java.util.Properties; +import java.util.concurrent.TimeUnit; public class ConsensusCommitAdminRepairTableIntegrationTestWithJdbcDatabase extends ConsensusCommitAdminRepairTableIntegrationTestBase { + @LazyInit private RdbEngineStrategy rdbEngine; @Override protected Properties getProps(String testName) { return JdbcEnv.getProperties(testName); } + @Override + protected void initialize(String testName) throws Exception { + super.initialize(testName); + Properties properties = getProperties(testName); + rdbEngine = RdbEngineFactory.create(new JdbcConfig(new DatabaseConfig(properties))); + } + @Override protected AdminTestUtils getAdminTestUtils(String testName) { return new JdbcAdminTestUtils(getProperties(testName)); } + + @Override + protected void waitForDifferentSessionDdl() { + if (JdbcTestUtils.isYugabyte(rdbEngine)) { + // This is needed to avoid schema or catalog version mismatch database errors. + Uninterruptibles.sleepUninterruptibly(1000, TimeUnit.MILLISECONDS); + return; + } + super.waitForDifferentSessionDdl(); + } } diff --git a/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminImportTestUtils.java b/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminImportTestUtils.java index aadd17a6eb..b33d72f47b 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminImportTestUtils.java +++ b/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminImportTestUtils.java @@ -145,7 +145,7 @@ public List createExistingDatabaseWithAllDataTypes(String namespace) } public void createExistingDatabase(String namespace) throws SQLException { - execute(rdbEngine.createNamespaceSqls(rdbEngine.enclose(namespace))); + execute(rdbEngine.createSchemaSqls(namespace)); } public List getIntCompatibleColumnNamesOnExistingDatabase(String table) { diff --git a/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminTestUtils.java b/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminTestUtils.java index 47f2a51940..63c15aeded 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminTestUtils.java +++ b/core/src/integration-test/java/com/scalar/db/storage/jdbc/JdbcAdminTestUtils.java @@ -31,7 +31,8 @@ public JdbcAdminTestUtils(Properties properties) { @Override public void dropMetadataTable() throws SQLException { execute( - "DROP TABLE " + rdbEngine.encloseFullTableName(metadataSchema, JdbcAdmin.METADATA_TABLE)); + "DROP TABLE " + + rdbEngine.encloseFullTableName(metadataSchema, TableMetadataService.TABLE_NAME)); String dropNamespaceStatement = rdbEngine.dropNamespaceSql(metadataSchema); execute(dropNamespaceStatement); @@ -40,7 +41,7 @@ public void dropMetadataTable() throws SQLException { @Override public void truncateMetadataTable() throws Exception { String truncateTableStatement = - rdbEngine.truncateTableSql(metadataSchema, JdbcAdmin.METADATA_TABLE); + rdbEngine.truncateTableSql(metadataSchema, TableMetadataService.TABLE_NAME); execute(truncateTableStatement); } @@ -49,7 +50,7 @@ public void truncateMetadataTable() throws Exception { public void corruptMetadata(String namespace, String table) throws Exception { String insertCorruptedMetadataStatement = "INSERT INTO " - + rdbEngine.encloseFullTableName(metadataSchema, JdbcAdmin.METADATA_TABLE) + + rdbEngine.encloseFullTableName(metadataSchema, TableMetadataService.TABLE_NAME) + " VALUES ('" + getFullTableName(namespace, table) + "','corrupted','corrupted','corrupted','corrupted','0','0')"; @@ -60,9 +61,9 @@ public void corruptMetadata(String namespace, String table) throws Exception { public void deleteMetadata(String namespace, String table) throws Exception { String deleteMetadataStatement = "DELETE FROM " - + rdbEngine.encloseFullTableName(metadataSchema, JdbcAdmin.METADATA_TABLE) + + rdbEngine.encloseFullTableName(metadataSchema, TableMetadataService.TABLE_NAME) + " WHERE " - + rdbEngine.enclose(JdbcAdmin.METADATA_COL_FULL_TABLE_NAME) + + rdbEngine.enclose(TableMetadataService.COL_FULL_TABLE_NAME) + " = ?"; try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = diff --git a/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageAdminTestUtils.java b/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageAdminTestUtils.java index 20dcf7b6c4..df54e95599 100644 --- a/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageAdminTestUtils.java +++ b/core/src/integration-test/java/com/scalar/db/storage/multistorage/MultiStorageAdminTestUtils.java @@ -6,13 +6,12 @@ import com.datastax.driver.core.schemabuilder.SchemaBuilder; import com.scalar.db.config.DatabaseConfig; import com.scalar.db.storage.cassandra.ClusterManager; -import com.scalar.db.storage.jdbc.JdbcAdmin; import com.scalar.db.storage.jdbc.JdbcConfig; import com.scalar.db.storage.jdbc.JdbcTestUtils; import com.scalar.db.storage.jdbc.JdbcUtils; import com.scalar.db.storage.jdbc.RdbEngineFactory; -import com.scalar.db.storage.jdbc.RdbEngineOracle; import com.scalar.db.storage.jdbc.RdbEngineStrategy; +import com.scalar.db.storage.jdbc.TableMetadataService; import com.scalar.db.util.AdminTestUtils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.sql.Connection; @@ -52,14 +51,9 @@ public void dropMetadataTable() throws SQLException { // for JDBC execute( "DROP TABLE " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.METADATA_TABLE)); + + rdbEngine.encloseFullTableName(jdbcMetadataSchema, TableMetadataService.TABLE_NAME)); - String dropNamespaceStatement; - if (rdbEngine instanceof RdbEngineOracle) { - dropNamespaceStatement = "DROP USER " + rdbEngine.enclose(jdbcMetadataSchema); - } else { - dropNamespaceStatement = "DROP SCHEMA " + rdbEngine.enclose(jdbcMetadataSchema); - } + String dropNamespaceStatement = rdbEngine.dropNamespaceSql(jdbcMetadataSchema); execute(dropNamespaceStatement); } @@ -70,7 +64,7 @@ public void truncateMetadataTable() throws Exception { // for JDBC String truncateTableStatement = "TRUNCATE TABLE " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.METADATA_TABLE); + + rdbEngine.encloseFullTableName(jdbcMetadataSchema, TableMetadataService.TABLE_NAME); execute(truncateTableStatement); } @@ -82,7 +76,7 @@ public void corruptMetadata(String namespace, String table) throws Exception { // for JDBC String insertCorruptedMetadataStatement = "INSERT INTO " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.METADATA_TABLE) + + rdbEngine.encloseFullTableName(jdbcMetadataSchema, TableMetadataService.TABLE_NAME) + " VALUES ('" + getFullTableName(namespace, table) + "','corrupted','corrupted','corrupted','corrupted','0','0')"; @@ -96,9 +90,9 @@ public void deleteMetadata(String namespace, String table) throws Exception { // for JDBC String deleteMetadataStatement = "DELETE FROM " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.METADATA_TABLE) + + rdbEngine.encloseFullTableName(jdbcMetadataSchema, TableMetadataService.TABLE_NAME) + " WHERE " - + rdbEngine.enclose(JdbcAdmin.METADATA_COL_FULL_TABLE_NAME) + + rdbEngine.enclose(TableMetadataService.COL_FULL_TABLE_NAME) + " = ?"; try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = diff --git a/core/src/main/java/com/scalar/db/common/CommonDistributedStorageAdmin.java b/core/src/main/java/com/scalar/db/common/CommonDistributedStorageAdmin.java index a052cff0cf..d75edd5d1a 100644 --- a/core/src/main/java/com/scalar/db/common/CommonDistributedStorageAdmin.java +++ b/core/src/main/java/com/scalar/db/common/CommonDistributedStorageAdmin.java @@ -464,7 +464,7 @@ public Set getNamespaceNames() throws ExecutionException { @Override public StorageInfo getStorageInfo(String namespace) throws ExecutionException { - if (!namespaceExists(namespace)) { + if (checkNamespace && !namespaceExists(namespace)) { throw new IllegalArgumentException(CoreError.NAMESPACE_NOT_FOUND.buildMessage(namespace)); } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java index 451759a6f2..9584e9cdca 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java @@ -9,8 +9,6 @@ import com.google.common.collect.Sets; import com.google.inject.Inject; import com.scalar.db.api.DistributedStorageAdmin; -import com.scalar.db.api.Scan; -import com.scalar.db.api.Scan.Ordering; import com.scalar.db.api.Scan.Ordering.Order; import com.scalar.db.api.StorageInfo; import com.scalar.db.api.TableMetadata; @@ -25,6 +23,7 @@ import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLWarning; import java.sql.Statement; import java.util.Collections; import java.util.HashSet; @@ -39,24 +38,17 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@SuppressFBWarnings({"OBL_UNSATISFIED_OBLIGATION", "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE"}) +@SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION") @ThreadSafe public class JdbcAdmin implements DistributedStorageAdmin { - public static final String METADATA_TABLE = "metadata"; - - @VisibleForTesting public static final String METADATA_COL_FULL_TABLE_NAME = "full_table_name"; - @VisibleForTesting static final String METADATA_COL_COLUMN_NAME = "column_name"; - @VisibleForTesting static final String METADATA_COL_DATA_TYPE = "data_type"; - @VisibleForTesting static final String METADATA_COL_KEY_TYPE = "key_type"; - @VisibleForTesting static final String METADATA_COL_CLUSTERING_ORDER = "clustering_order"; - @VisibleForTesting static final String METADATA_COL_INDEXED = "indexed"; - @VisibleForTesting static final String METADATA_COL_ORDINAL_POSITION = "ordinal_position"; + private static final Logger logger = LoggerFactory.getLogger(JdbcAdmin.class); + @VisibleForTesting static final String JDBC_COL_COLUMN_NAME = "COLUMN_NAME"; @VisibleForTesting static final String JDBC_COL_DATA_TYPE = "DATA_TYPE"; @VisibleForTesting static final String JDBC_COL_TYPE_NAME = "TYPE_NAME"; @VisibleForTesting static final String JDBC_COL_COLUMN_SIZE = "COLUMN_SIZE"; @VisibleForTesting static final String JDBC_COL_DECIMAL_DIGITS = "DECIMAL_DIGITS"; - private static final Logger logger = LoggerFactory.getLogger(JdbcAdmin.class); + private static final String INDEX_NAME_PREFIX = "index"; private static final StorageInfo STORAGE_INFO = new StorageInfoImpl( @@ -68,6 +60,7 @@ public class JdbcAdmin implements DistributedStorageAdmin { private final RdbEngineStrategy rdbEngine; private final BasicDataSource dataSource; private final String metadataSchema; + private final TableMetadataService tableMetadataService; @Inject public JdbcAdmin(DatabaseConfig databaseConfig) { @@ -76,6 +69,7 @@ public JdbcAdmin(DatabaseConfig databaseConfig) { dataSource = JdbcUtils.initDataSourceForAdmin(config, rdbEngine); metadataSchema = config.getTableMetadataSchema().orElse(DatabaseConfig.DEFAULT_SYSTEM_NAMESPACE_NAME); + tableMetadataService = new TableMetadataService(metadataSchema, rdbEngine); } @SuppressFBWarnings("EI_EXPOSE_REP2") @@ -84,6 +78,26 @@ public JdbcAdmin(BasicDataSource dataSource, JdbcConfig config) { this.dataSource = dataSource; metadataSchema = config.getTableMetadataSchema().orElse(DatabaseConfig.DEFAULT_SYSTEM_NAMESPACE_NAME); + tableMetadataService = new TableMetadataService(metadataSchema, rdbEngine); + } + + private static boolean hasDescClusteringOrder(TableMetadata metadata) { + return metadata.getClusteringKeyNames().stream() + .anyMatch(c -> metadata.getClusteringOrder(c) == Order.DESC); + } + + @VisibleForTesting + static boolean hasDifferentClusteringOrders(TableMetadata metadata) { + boolean hasAscOrder = false; + boolean hasDescOrder = false; + for (Order order : metadata.getClusteringOrders().values()) { + if (order == Order.ASC) { + hasAscOrder = true; + } else { + hasDescOrder = true; + } + } + return hasAscOrder && hasDescOrder; } @Override @@ -91,11 +105,10 @@ public void createNamespace(String namespace, Map options) throws ExecutionException { rdbEngine.throwIfInvalidNamespaceName(namespace); - String fullNamespace = enclose(namespace); try (Connection connection = dataSource.getConnection()) { - execute(connection, rdbEngine.createNamespaceSqls(fullNamespace)); + execute(connection, rdbEngine.createSchemaSqls(namespace)); } catch (SQLException e) { - throw new ExecutionException("Creating the schema failed", e); + throw new ExecutionException("Creating the " + namespace + " schema failed", e); } } @@ -103,24 +116,25 @@ public void createNamespace(String namespace, Map options) public void createTable( String namespace, String table, TableMetadata metadata, Map options) throws ExecutionException { - rdbEngine.throwIfInvalidTableName(table); - try (Connection connection = dataSource.getConnection()) { - // Create the metadata schema and table first if they do not exist - createMetadataSchemaAndTableIfNotExists(connection); - - createTableInternal(connection, namespace, table, metadata); - createIndex(connection, namespace, table, metadata); - addTableMetadata(connection, namespace, table, metadata, false); + createTableInternal(connection, namespace, table, metadata, false); + addTableMetadata(connection, namespace, table, metadata, true, false); } catch (SQLException e) { throw new ExecutionException( - "Creating the table failed: " + getFullTableName(namespace, table), e); + "Creating the " + getFullTableName(namespace, table) + " table failed ", e); } } - private void createTableInternal( - Connection connection, String schema, String table, TableMetadata metadata) + @VisibleForTesting + void createTableInternal( + Connection connection, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) throws SQLException { + rdbEngine.throwIfInvalidTableName(table); + String createTableStatement = "CREATE TABLE " + encloseFullTableName(schema, table) + "("; // Order the columns for their creation by (partition keys >> clustering keys >> other columns) LinkedHashSet sortedColumnNames = @@ -138,182 +152,54 @@ private void createTableInternal( .collect(Collectors.joining(",")); // Add primary key definition - boolean hasDescClusteringOrder = hasDescClusteringOrder(metadata); createTableStatement += - ", " + rdbEngine.createTableInternalPrimaryKeyClause(hasDescClusteringOrder, metadata); - execute(connection, createTableStatement); - - String[] sqls = - rdbEngine.createTableInternalSqlsAfterCreateTable( - hasDifferentClusteringOrders(metadata), schema, table, metadata); - execute(connection, sqls); - } - - private void createIndex( - Connection connection, String schema, String table, TableMetadata metadata) - throws SQLException { - for (String indexedColumn : metadata.getSecondaryIndexNames()) { - createIndex(connection, schema, table, indexedColumn); - } + ", " + + rdbEngine.createTableInternalPrimaryKeyClause( + hasDescClusteringOrder(metadata), metadata); + createTable(connection, createTableStatement, ifNotExists); + createTableInternalSqlsAfterCreateTable(connection, schema, table, metadata, ifNotExists); + createIndex(connection, schema, table, metadata, ifNotExists); } - @VisibleForTesting - void addTableMetadata( + private void createTableInternalSqlsAfterCreateTable( Connection connection, - String namespace, + String schema, String table, TableMetadata metadata, - boolean createMetadataTableIfNotExists) + boolean ifNotExists) throws SQLException { - if (createMetadataTableIfNotExists) { - createMetadataSchemaAndTableIfNotExists(connection); - } - LinkedHashSet orderedColumns = new LinkedHashSet<>(metadata.getPartitionKeyNames()); - orderedColumns.addAll(metadata.getClusteringKeyNames()); - orderedColumns.addAll(metadata.getColumnNames()); - int ordinalPosition = 1; - for (String column : orderedColumns) { - insertMetadataColumn(namespace, table, metadata, connection, ordinalPosition++, column); - } - } - - private void createMetadataSchemaAndTableIfNotExists(Connection connection) throws SQLException { - createMetadataSchemaIfNotExists(connection); - createMetadataTableIfNotExists(connection); - } - - private void createMetadataSchemaIfNotExists(Connection connection) throws SQLException { - String[] sqls = rdbEngine.createMetadataSchemaIfNotExistsSql(metadataSchema); - try { - execute(connection, sqls); - } catch (SQLException e) { - // Suppress exceptions indicating the duplicate metadata schema - if (!rdbEngine.isCreateMetadataSchemaDuplicateSchemaError(e)) { - throw e; - } - } - } - - @VisibleForTesting - void createMetadataTableIfNotExists(Connection connection) throws SQLException { - String createTableStatement = - "CREATE TABLE " - + encloseFullTableName(metadataSchema, METADATA_TABLE) - + "(" - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " " - + getTextType(128, true) - + "," - + enclose(METADATA_COL_COLUMN_NAME) - + " " - + getTextType(128, true) - + "," - + enclose(METADATA_COL_DATA_TYPE) - + " " - + getTextType(20, false) - + " NOT NULL," - + enclose(METADATA_COL_KEY_TYPE) - + " " - + getTextType(20, false) - + "," - + enclose(METADATA_COL_CLUSTERING_ORDER) - + " " - + getTextType(10, false) - + "," - + enclose(METADATA_COL_INDEXED) - + " " - + getBooleanType() - + " NOT NULL," - + enclose(METADATA_COL_ORDINAL_POSITION) - + " INTEGER NOT NULL," - + "PRIMARY KEY (" - + enclose(METADATA_COL_FULL_TABLE_NAME) - + ", " - + enclose(METADATA_COL_COLUMN_NAME) - + "))"; - - String stmt = rdbEngine.tryAddIfNotExistsToCreateTableSql(createTableStatement); + String[] stmts = + rdbEngine.createTableInternalSqlsAfterCreateTable( + hasDifferentClusteringOrders(metadata), schema, table, metadata, ifNotExists); try { - execute(connection, stmt); + execute(connection, stmts, ifNotExists ? null : rdbEngine::throwIfDuplicatedIndexWarning); } catch (SQLException e) { - // Suppress the exception thrown when the table already exists - if (!rdbEngine.isDuplicateTableError(e)) { + if (!(ifNotExists && rdbEngine.isDuplicateIndexError(e))) { throw e; } } } - private String getTextType(int charLength, boolean isKey) { - return rdbEngine.getTextType(charLength, isKey); - } - - private String getBooleanType() { - return rdbEngine.getDataTypeForEngine(DataType.BOOLEAN); - } - - private void insertMetadataColumn( + private void createIndex( + Connection connection, String schema, String table, TableMetadata metadata, - Connection connection, - int ordinalPosition, - String column) + boolean ifNotExists) throws SQLException { - KeyType keyType = null; - if (metadata.getPartitionKeyNames().contains(column)) { - keyType = KeyType.PARTITION; - } - if (metadata.getClusteringKeyNames().contains(column)) { - keyType = KeyType.CLUSTERING; + for (String indexedColumn : metadata.getSecondaryIndexNames()) { + createIndex(connection, schema, table, indexedColumn, ifNotExists); } - - String insertStatement = - getInsertStatement( - schema, - table, - column, - metadata.getColumnDataType(column), - keyType, - metadata.getClusteringOrder(column), - metadata.getSecondaryIndexNames().contains(column), - ordinalPosition); - execute(connection, insertStatement); - } - - private String getInsertStatement( - String schema, - String table, - String columnName, - DataType dataType, - @Nullable KeyType keyType, - @Nullable Ordering.Order ckOrder, - boolean indexed, - int ordinalPosition) { - - return String.format( - "INSERT INTO %s VALUES ('%s','%s','%s',%s,%s,%s,%d)", - encloseFullTableName(metadataSchema, METADATA_TABLE), - getFullTableName(schema, table), - columnName, - dataType.toString(), - keyType != null ? "'" + keyType + "'" : "NULL", - ckOrder != null ? "'" + ckOrder + "'" : "NULL", - computeBooleanValue(indexed), - ordinalPosition); - } - - private String computeBooleanValue(boolean value) { - return rdbEngine.computeBooleanValue(value); } @Override public void dropTable(String namespace, String table) throws ExecutionException { try (Connection connection = dataSource.getConnection()) { dropTableInternal(connection, namespace, table); - deleteTableMetadata(connection, namespace, table, true); + tableMetadataService.deleteTableMetadata(connection, namespace, table, true); } catch (SQLException e) { throw new ExecutionException( - "Dropping the table failed: " + getFullTableName(namespace, table), e); + "Dropping the " + getFullTableName(namespace, table) + " table failed", e); } } @@ -323,63 +209,6 @@ private void dropTableInternal(Connection connection, String schema, String tabl execute(connection, dropTableStatement); } - private void deleteTableMetadata( - Connection connection, String namespace, String table, boolean deleteMetadataTableIfEmpty) - throws SQLException { - try { - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - if (deleteMetadataTableIfEmpty) { - deleteMetadataSchemaAndTableIfEmpty(connection); - } - } catch (SQLException e) { - if (e.getMessage().contains("Unknown table") || e.getMessage().contains("does not exist")) { - return; - } - throw e; - } - } - - private String getDeleteTableMetadataStatement(String schema, String table) { - return "DELETE FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE) - + " WHERE " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " = '" - + getFullTableName(schema, table) - + "'"; - } - - private void deleteMetadataSchemaAndTableIfEmpty(Connection connection) throws SQLException { - if (isMetadataTableEmpty(connection)) { - deleteMetadataTable(connection); - deleteMetadataSchema(connection); - } - } - - private boolean isMetadataTableEmpty(Connection connection) throws SQLException { - String selectAllTables = - "SELECT DISTINCT " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE); - try (Statement statement = connection.createStatement(); - ResultSet results = statement.executeQuery(selectAllTables)) { - return !results.next(); - } - } - - private void deleteMetadataTable(Connection connection) throws SQLException { - String dropTableStatement = - "DROP TABLE " + encloseFullTableName(metadataSchema, METADATA_TABLE); - - execute(connection, dropTableStatement); - } - - private void deleteMetadataSchema(Connection connection) throws SQLException { - String sql = rdbEngine.deleteMetadataSchemaSql(metadataSchema); - execute(connection, sql); - } - @Override public void dropNamespace(String namespace) throws ExecutionException { try (Connection connection = dataSource.getConnection()) { @@ -395,6 +224,24 @@ public void dropNamespace(String namespace) throws ExecutionException { } } + private Set getInternalTableNames(Connection connection, String namespace) + throws SQLException { + String sql = rdbEngine.getTableNamesInNamespaceSql(); + if (Strings.isNullOrEmpty(sql)) { + return Collections.emptySet(); + } + Set tableNames = new HashSet<>(); + try (PreparedStatement statement = connection.prepareStatement(sql)) { + statement.setString(1, namespace); + try (ResultSet resultSet = statement.executeQuery()) { + while (resultSet.next()) { + tableNames.add(resultSet.getString(1)); + } + } + } + return tableNames; + } + @Override public void truncateTable(String namespace, String table) throws ExecutionException { String truncateTableStatement = rdbEngine.truncateTableSql(namespace, table); @@ -402,68 +249,22 @@ public void truncateTable(String namespace, String table) throws ExecutionExcept execute(connection, truncateTableStatement); } catch (SQLException e) { throw new ExecutionException( - "Truncating the table failed: " + getFullTableName(namespace, table), e); + "Truncating the " + getFullTableName(namespace, table) + " table failed", e); } } @Override public TableMetadata getTableMetadata(String namespace, String table) throws ExecutionException { - TableMetadata.Builder builder = TableMetadata.newBuilder(); - boolean tableExists = false; - try (Connection connection = dataSource.getConnection()) { rdbEngine.setConnectionToReadOnly(connection, true); - - if (!namespaceExistsInternal(connection, metadataSchema)) { - return null; - } - - try (PreparedStatement preparedStatement = - connection.prepareStatement(getSelectColumnsStatement())) { - preparedStatement.setString(1, getFullTableName(namespace, table)); - - try (ResultSet resultSet = preparedStatement.executeQuery()) { - while (resultSet.next()) { - tableExists = true; - - String columnName = resultSet.getString(METADATA_COL_COLUMN_NAME); - DataType dataType = DataType.valueOf(resultSet.getString(METADATA_COL_DATA_TYPE)); - builder.addColumn(columnName, dataType); - - boolean indexed = resultSet.getBoolean(METADATA_COL_INDEXED); - if (indexed) { - builder.addSecondaryIndex(columnName); - } - - String keyType = resultSet.getString(METADATA_COL_KEY_TYPE); - if (keyType == null) { - continue; - } - - switch (KeyType.valueOf(keyType)) { - case PARTITION: - builder.addPartitionKey(columnName); - break; - case CLUSTERING: - Scan.Ordering.Order clusteringOrder = - Scan.Ordering.Order.valueOf(resultSet.getString(METADATA_COL_CLUSTERING_ORDER)); - builder.addClusteringKey(columnName, clusteringOrder); - break; - default: - throw new AssertionError("Invalid key type: " + keyType); - } - } - } - } + return tableMetadataService.getTableMetadata(connection, namespace, table); } catch (SQLException e) { - throw new ExecutionException("Getting a table metadata failed", e); - } - - if (!tableExists) { - return null; + throw new ExecutionException( + "Getting a table metadata for the " + + getFullTableName(namespace, table) + + " table failed", + e); } - - return builder.build(); } @VisibleForTesting @@ -513,7 +314,11 @@ TableMetadata getImportTableMetadata( builder.addColumn(columnName, dataType); } } catch (SQLException e) { - throw new ExecutionException("Getting a table metadata failed", e); + throw new ExecutionException( + "Getting a table metadata for the " + + getFullTableName(namespace, table) + + " table failed", + e); } return builder.build(); @@ -530,86 +335,22 @@ public void importTable( try (Connection connection = dataSource.getConnection()) { TableMetadata tableMetadata = getImportTableMetadata(namespace, table, overrideColumnsType); - addTableMetadata(connection, namespace, table, tableMetadata, true); + addTableMetadata(connection, namespace, table, tableMetadata, true, false); } catch (SQLException | ExecutionException e) { throw new ExecutionException( String.format("Importing the %s table failed", getFullTableName(namespace, table)), e); } } - private String getSelectColumnsStatement() { - return "SELECT " - + enclose(METADATA_COL_COLUMN_NAME) - + "," - + enclose(METADATA_COL_DATA_TYPE) - + "," - + enclose(METADATA_COL_KEY_TYPE) - + "," - + enclose(METADATA_COL_CLUSTERING_ORDER) - + "," - + enclose(METADATA_COL_INDEXED) - + " FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE) - + " WHERE " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + "=? ORDER BY " - + enclose(METADATA_COL_ORDINAL_POSITION) - + " ASC"; - } - @Override public Set getNamespaceTableNames(String namespace) throws ExecutionException { - String selectTablesOfNamespaceStatement = - "SELECT DISTINCT " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE) - + " WHERE " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " LIKE ?"; try (Connection connection = dataSource.getConnection()) { rdbEngine.setConnectionToReadOnly(connection, true); - - try (PreparedStatement preparedStatement = - connection.prepareStatement(selectTablesOfNamespaceStatement)) { - String prefix = namespace + "."; - preparedStatement.setString(1, prefix + "%"); - try (ResultSet results = preparedStatement.executeQuery()) { - Set tableNames = new HashSet<>(); - while (results.next()) { - String tableName = - results.getString(METADATA_COL_FULL_TABLE_NAME).substring(prefix.length()); - tableNames.add(tableName); - } - return tableNames; - } - } + return tableMetadataService.getNamespaceTableNames(connection, namespace); } catch (SQLException e) { - // An exception will be thrown if the metadata table does not exist when executing the select - // query - if (rdbEngine.isUndefinedTableError(e)) { - return Collections.emptySet(); - } - throw new ExecutionException("Retrieving the namespace table names failed", e); - } - } - - private Set getInternalTableNames(Connection connection, String namespace) - throws SQLException { - String sql = rdbEngine.getTableNamesInNamespaceSql(); - if (Strings.isNullOrEmpty(sql)) { - return Collections.emptySet(); - } - Set tableNames = new HashSet<>(); - try (PreparedStatement statement = connection.prepareStatement(sql)) { - statement.setString(1, namespace); - try (ResultSet resultSet = statement.executeQuery()) { - while (resultSet.next()) { - tableNames.add(resultSet.getString(1)); - } - } + throw new ExecutionException( + "Getting the list of tables of the " + namespace + " schema failed", e); } - return tableNames; } @Override @@ -669,35 +410,20 @@ private String getVendorDbColumnType(TableMetadata metadata, String columnName) } } - private static boolean hasDescClusteringOrder(TableMetadata metadata) { - return metadata.getClusteringKeyNames().stream() - .anyMatch(c -> metadata.getClusteringOrder(c) == Order.DESC); - } - - @VisibleForTesting - static boolean hasDifferentClusteringOrders(TableMetadata metadata) { - boolean hasAscOrder = false; - boolean hasDescOrder = false; - for (Order order : metadata.getClusteringOrders().values()) { - if (order == Order.ASC) { - hasAscOrder = true; - } else { - hasDescOrder = true; - } - } - return hasAscOrder && hasDescOrder; - } - @Override public void createIndex( String namespace, String table, String columnName, Map options) throws ExecutionException { try (Connection connection = dataSource.getConnection()) { alterToIndexColumnTypeIfNecessary(connection, namespace, table, columnName); - createIndex(connection, namespace, table, columnName); - updateTableMetadata(connection, namespace, table, columnName, true); + createIndex(connection, namespace, table, columnName, false); + tableMetadataService.updateTableMetadata(connection, namespace, table, columnName, true); } catch (ExecutionException | SQLException e) { - throw new ExecutionException("Creating the secondary index failed", e); + throw new ExecutionException( + String.format( + "Creating the secondary index on the %s column for the %s table failed", + columnName, getFullTableName(namespace, table)), + e); } } @@ -741,28 +467,12 @@ public void dropIndex(String namespace, String table, String columnName) try (Connection connection = dataSource.getConnection()) { dropIndex(connection, namespace, table, columnName); alterToRegularColumnTypeIfNecessary(connection, namespace, table, columnName); - updateTableMetadata(connection, namespace, table, columnName, false); + tableMetadataService.updateTableMetadata(connection, namespace, table, columnName, false); } catch (SQLException e) { - throw new ExecutionException("Dropping the secondary index failed", e); - } - } - - private boolean tableExistsInternal(Connection connection, String namespace, String table) - throws ExecutionException { - String fullTableName = encloseFullTableName(namespace, table); - String sql = rdbEngine.tableExistsInternalTableCheckSql(fullTableName); - try { - execute(connection, sql); - return true; - } catch (SQLException e) { - // An exception will be thrown if the table does not exist when executing the select - // query - if (rdbEngine.isUndefinedTableError(e)) { - return false; - } throw new ExecutionException( String.format( - "Checking if the %s table exists failed", getFullTableName(namespace, table)), + "Dropping the secondary index on the %s column for the %s table failed", + columnName, getFullTableName(namespace, table)), e); } } @@ -779,17 +489,10 @@ public void repairTable( CoreError.TABLE_NOT_FOUND.buildMessage(getFullTableName(namespace, table))); } - if (tableExistsInternal(connection, metadataSchema, METADATA_TABLE)) { - // Delete then add the metadata for the table - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - addTableMetadata(connection, namespace, table, metadata, false); - } else { - // Create the metadata table then add the metadata for the table - addTableMetadata(connection, namespace, table, metadata, true); - } - } catch (ExecutionException | SQLException e) { + addTableMetadata(connection, namespace, table, metadata, true, true); + } catch (SQLException e) { throw new ExecutionException( - String.format("Repairing the table %s.%s failed", namespace, table), e); + "Repairing the " + getFullTableName(namespace, table) + " table failed ", e); } } @@ -810,13 +513,13 @@ public void addNewColumnToTable( + getVendorDbColumnType(updatedTableMetadata, columnName); try (Connection connection = dataSource.getConnection()) { execute(connection, addNewColumnStatement); - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - addTableMetadata(connection, namespace, table, updatedTableMetadata, false); + addTableMetadata(connection, namespace, table, updatedTableMetadata, false, true); } } catch (SQLException e) { throw new ExecutionException( String.format( - "Adding the new column %s to the %s.%s table failed", columnName, namespace, table), + "Adding the new %s column to the %s table failed", + columnName, getFullTableName(namespace, table)), e); } } @@ -833,8 +536,7 @@ public void dropColumnFromTable(String namespace, String table, String columnNam for (String dropColumnStatement : dropColumnStatements) { execute(connection, dropColumnStatement); } - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - addTableMetadata(connection, namespace, table, updatedTableMetadata, false); + addTableMetadata(connection, namespace, table, updatedTableMetadata, false, true); } } catch (SQLException e) { throw new ExecutionException( @@ -880,8 +582,7 @@ public void renameColumn( renameIndexInternal( connection, namespace, table, newColumnName, oldIndexName, newIndexName); } - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - addTableMetadata(connection, namespace, table, updatedTableMetadata, false); + addTableMetadata(connection, namespace, table, updatedTableMetadata, false, true); } } catch (SQLException e) { throw new ExecutionException( @@ -915,8 +616,7 @@ public void alterColumnType( for (String alterColumnTypeStatement : alterColumnTypeStatements) { execute(connection, alterColumnTypeStatement); } - execute(connection, getDeleteTableMetadataStatement(namespace, table)); - addTableMetadata(connection, namespace, table, updatedTableMetadata, false); + addTableMetadata(connection, namespace, table, updatedTableMetadata, false, true); } } catch (SQLException e) { throw new ExecutionException( @@ -930,20 +630,22 @@ columnName, newColumnType, getFullTableName(namespace, table)), @Override public void renameTable(String namespace, String oldTableName, String newTableName) throws ExecutionException { + rdbEngine.throwIfInvalidTableName(newTableName); + try { TableMetadata tableMetadata = getTableMetadata(namespace, oldTableName); assert tableMetadata != null; String renameTableStatement = rdbEngine.renameTableSql(namespace, oldTableName, newTableName); try (Connection connection = dataSource.getConnection()) { execute(connection, renameTableStatement); - deleteTableMetadata(connection, namespace, oldTableName, false); + tableMetadataService.deleteTableMetadata(connection, namespace, oldTableName, false); for (String indexedColumnName : tableMetadata.getSecondaryIndexNames()) { String oldIndexName = getIndexName(namespace, oldTableName, indexedColumnName); String newIndexName = getIndexName(namespace, newTableName, indexedColumnName); renameIndexInternal( connection, namespace, newTableName, indexedColumnName, oldIndexName, newIndexName); } - addTableMetadata(connection, namespace, newTableName, tableMetadata, false); + addTableMetadata(connection, namespace, newTableName, tableMetadata, false, false); } } catch (SQLException e) { throw new ExecutionException( @@ -954,43 +656,40 @@ public void renameTable(String namespace, String oldTableName, String newTableNa } } - @Override - public Set getNamespaceNames() throws ExecutionException { - String selectAllTableNames = - "SELECT DISTINCT " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE); - try (Connection connection = dataSource.getConnection()) { - rdbEngine.setConnectionToReadOnly(connection, true); - - try (Statement stmt = connection.createStatement(); - ResultSet rs = stmt.executeQuery(selectAllTableNames)) { - Set namespaceOfExistingTables = new HashSet<>(); - while (rs.next()) { - String fullTableName = rs.getString(METADATA_COL_FULL_TABLE_NAME); - String namespaceName = fullTableName.substring(0, fullTableName.indexOf('.')); - namespaceOfExistingTables.add(namespaceName); - } - namespaceOfExistingTables.add(metadataSchema); - - return namespaceOfExistingTables; - } - } catch (SQLException e) { - // An exception will be thrown if the namespace table does not exist when executing the select - // query - if (rdbEngine.isUndefinedTableError(e)) { - return Collections.singleton(metadataSchema); - } - throw new ExecutionException("Getting the existing schema names failed", e); + private void renameIndexInternal( + Connection connection, + String schema, + String table, + String column, + String oldIndexName, + String newIndexName) + throws SQLException { + String[] sqls = rdbEngine.renameIndexSqls(schema, table, column, oldIndexName, newIndexName); + for (String sql : sqls) { + execute(connection, sql); } } - private void createIndex(Connection connection, String schema, String table, String indexedColumn) + @VisibleForTesting + void createIndex( + Connection connection, String schema, String table, String indexedColumn, boolean ifNotExists) throws SQLException { String indexName = getIndexName(schema, table, indexedColumn); String createIndexStatement = rdbEngine.createIndexSql(schema, table, indexName, indexedColumn); - execute(connection, createIndexStatement); + if (ifNotExists) { + createIndexStatement = rdbEngine.tryAddIfNotExistsToCreateIndexSql(createIndexStatement); + } + try { + execute( + connection, + createIndexStatement, + ifNotExists ? null : rdbEngine::throwIfDuplicatedIndexWarning); + } catch (SQLException e) { + // Suppress the exception thrown when the index already exists + if (!(ifNotExists && rdbEngine.isDuplicateIndexError(e))) { + throw e; + } + } } private void dropIndex(Connection connection, String schema, String table, String indexedColumn) @@ -1000,71 +699,128 @@ private void dropIndex(Connection connection, String schema, String table, Strin execute(connection, sql); } - private void renameIndexInternal( + private String getIndexName(String schema, String table, String indexedColumn) { + return String.join("_", INDEX_NAME_PREFIX, schema, table, indexedColumn); + } + + @Override + public Set getNamespaceNames() throws ExecutionException { + try (Connection connection = dataSource.getConnection()) { + rdbEngine.setConnectionToReadOnly(connection, true); + return tableMetadataService.getNamespaceNames(connection); + } catch (SQLException e) { + throw new ExecutionException("Getting the existing schema names failed", e); + } + } + + @Override + public StorageInfo getStorageInfo(String namespace) { + return STORAGE_INFO; + } + + @VisibleForTesting + void addTableMetadata( Connection connection, - String schema, + String namespace, String table, - String column, - String oldIndexName, - String newIndexName) + TableMetadata metadata, + boolean createMetadataTable, + boolean overwriteMetadata) throws SQLException { - String[] sqls = rdbEngine.renameIndexSqls(schema, table, column, oldIndexName, newIndexName); - for (String sql : sqls) { + tableMetadataService.addTableMetadata( + connection, namespace, table, metadata, createMetadataTable, overwriteMetadata); + } + + @VisibleForTesting + void createTableMetadataTableIfNotExists(Connection connection) throws SQLException { + tableMetadataService.createTableMetadataTableIfNotExists(connection); + } + + private void createTable(Connection connection, String createTableStatement, boolean ifNotExists) + throws SQLException { + String stmt = createTableStatement; + if (ifNotExists) { + stmt = rdbEngine.tryAddIfNotExistsToCreateTableSql(createTableStatement); + } + try { + execute(connection, stmt); + } catch (SQLException e) { + // Suppress the exception thrown when the table already exists + if (!(ifNotExists && rdbEngine.isDuplicateTableError(e))) { + throw e; + } + } + } + + private boolean tableExistsInternal(Connection connection, String namespace, String table) + throws ExecutionException { + String fullTableName = encloseFullTableName(namespace, table); + String sql = rdbEngine.tableExistsInternalTableCheckSql(fullTableName); + try { execute(connection, sql); + return true; + } catch (SQLException e) { + // An exception will be thrown if the table does not exist when executing the select + // query + if (rdbEngine.isUndefinedTableError(e)) { + return false; + } + throw new ExecutionException( + String.format( + "Checking if the %s table exists failed", getFullTableName(namespace, table)), + e); } } - private String getIndexName(String schema, String table, String indexedColumn) { - return String.join("_", INDEX_NAME_PREFIX, schema, table, indexedColumn); + private String enclose(String name) { + return rdbEngine.enclose(name); } - private void updateTableMetadata( - Connection connection, String schema, String table, String columnName, boolean indexed) - throws SQLException { - String updateStatement = - "UPDATE " - + encloseFullTableName(metadataSchema, METADATA_TABLE) - + " SET " - + enclose(METADATA_COL_INDEXED) - + "=" - + computeBooleanValue(indexed) - + " WHERE " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + "='" - + getFullTableName(schema, table) - + "' AND " - + enclose(METADATA_COL_COLUMN_NAME) - + "='" - + columnName - + "'"; - execute(connection, updateStatement); + private String encloseFullTableName(String schema, String table) { + return rdbEngine.encloseFullTableName(schema, table); } static void execute(Connection connection, String sql) throws SQLException { + execute(connection, sql, null); + } + + static void execute(Connection connection, String sql, @Nullable SqlWarningHandler handler) + throws SQLException { if (Strings.isNullOrEmpty(sql)) { return; } try (Statement stmt = connection.createStatement()) { stmt.execute(sql); + throwSqlWarningIfNeeded(handler, stmt); } } - static void execute(Connection connection, String[] sqls) throws SQLException { - for (String sql : sqls) { - execute(connection, sql); + private static void throwSqlWarningIfNeeded(SqlWarningHandler handler, Statement stmt) + throws SQLException { + if (handler == null) { + return; + } + SQLWarning warning = stmt.getWarnings(); + while (warning != null) { + handler.throwSqlWarningIfNeeded(warning); + warning = warning.getNextWarning(); } } - private String enclose(String name) { - return rdbEngine.enclose(name); + static void execute(Connection connection, String[] sqls) throws SQLException { + execute(connection, sqls, null); } - private String encloseFullTableName(String schema, String table) { - return rdbEngine.encloseFullTableName(schema, table); + static void execute( + Connection connection, String[] sqls, @Nullable SqlWarningHandler warningHandler) + throws SQLException { + for (String sql : sqls) { + execute(connection, sql, warningHandler); + } } - @Override - public StorageInfo getStorageInfo(String namespace) { - return STORAGE_INFO; + @FunctionalInterface + interface SqlWarningHandler { + void throwSqlWarningIfNeeded(SQLWarning warning) throws SQLException; } } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineDb2.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineDb2.java index 55971b7d85..770c73834e 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineDb2.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineDb2.java @@ -24,6 +24,7 @@ import java.sql.JDBCType; import java.sql.ResultSet; import java.sql.SQLException; +import java.sql.SQLWarning; import java.sql.Timestamp; import java.sql.Types; import java.time.Instant; @@ -172,8 +173,13 @@ public int getSqlTypes(DataType dataType) { } @Override - public String[] createNamespaceSqls(String fullNamespace) { - return new String[] {"CREATE SCHEMA " + fullNamespace}; + public String[] createSchemaSqls(String fullSchema) { + return new String[] {"CREATE SCHEMA " + enclose(fullSchema)}; + } + + @Override + public String[] createSchemaIfNotExistsSqls(String fullSchema) { + return createSchemaSqls(fullSchema); } @Override @@ -189,7 +195,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { ArrayList sqls = new ArrayList<>(); if (hasDifferentClusteringOrders) { @@ -217,22 +227,12 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS"); } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - return new String[] {"CREATE SCHEMA " + enclose(metadataSchema)}; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { // SQL error code -601: Name already used return e.getErrorCode() == -601; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - return dropNamespaceSql(metadataSchema); - } - @Override public String dropNamespaceSql(String namespace) { return "DROP SCHEMA " + enclose(namespace) + " RESTRICT"; @@ -244,6 +244,11 @@ public void dropNamespaceTranslateSQLException(SQLException e, String namespace) throw new ExecutionException("Dropping the schema failed: " + namespace, e); } + @Override + public String namespaceExistsStatement() { + return "SELECT 1 FROM syscat.schemata WHERE schemaname = ?"; + } + @Override public String[] dropColumnSql(String namespace, String table, String columnName) { return new String[] { @@ -314,6 +319,35 @@ public String[] renameIndexSqls( }; } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // Even though the "create index if exists ..." syntax does not exist, + // only a warning is raised when the index already exists but no error is thrown + // so we return false in any case + // Raised warning for duplicated index should be checked with + // RdbEngineDb2.throwIfDuplicatedIndexWarning() + return false; + } + + @Override + public void throwIfDuplicatedIndexWarning(SQLWarning warning) throws SQLException { + assert warning != null; + if (warning.getErrorCode() == 605) { + // Only a warning is raised when the index already exists but no exception is thrown by the + // driver. + // To match the behavior of other storages, we throw an exception in this case. + // + // SQL error code 605: The index name already exists + throw new SQLException( + warning.getMessage(), warning.getSQLState(), warning.getErrorCode(), warning.getCause()); + } + } + + @Override + public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { + return createIndexSql; + } + @Override public SelectQuery buildSelectWithLimitQuery(SelectQuery.Builder builder, int limit) { return new SelectWithLimitQuery(builder, limit); @@ -397,11 +431,6 @@ public String computeBooleanValue(boolean value) { return value ? "true" : "false"; } - @Override - public String namespaceExistsStatement() { - return "SELECT 1 FROM syscat.schemata WHERE schemaname = ?"; - } - @Override public DateColumn parseDateColumn(ResultSet resultSet, String columnName) throws SQLException { String dateStr = resultSet.getString(columnName); diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java index a6a86122d5..f5de0f7cd8 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineMysql.java @@ -46,8 +46,13 @@ class RdbEngineMysql extends AbstractRdbEngine { } @Override - public String[] createNamespaceSqls(String fullNamespace) { - return new String[] {"CREATE SCHEMA " + fullNamespace}; + public String[] createSchemaSqls(String fullSchema) { + return new String[] {"CREATE SCHEMA " + enclose(fullSchema)}; + } + + @Override + public String[] createSchemaIfNotExistsSqls(String schema) { + return new String[] {"CREATE SCHEMA IF NOT EXISTS " + enclose(schema)}; } @Override @@ -74,7 +79,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { // do nothing return new String[] {}; } @@ -84,21 +93,11 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS"); } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - return new String[] {"CREATE SCHEMA IF NOT EXISTS " + enclose(metadataSchema)}; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { return false; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - return "DROP SCHEMA " + enclose(metadataSchema); - } - @Override public String dropNamespaceSql(String namespace) { return "DROP SCHEMA " + enclose(namespace); @@ -235,6 +234,14 @@ public boolean isConflict(SQLException e) { return e.getErrorCode() == 1213 || e.getErrorCode() == 1205; } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html + // Error number: 1061; Symbol: ER_DUP_KEYNAME; SQLSTATE: 42000 + // Message: Duplicate key name '%s' + return e.getErrorCode() == 1061; + } + @Override public String getDataTypeForEngine(DataType scalarDbDataType) { switch (scalarDbDataType) { @@ -448,6 +455,11 @@ public String getEscape(LikeExpression likeExpression) { return escape.isEmpty() ? "\\" : escape; } + @Override + public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { + return createIndexSql; + } + @Nullable @Override public String getCatalogName(String namespace) { diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java index 67c5fd4a97..0dae8c68cd 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineOracle.java @@ -35,7 +35,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class RdbEngineOracle extends AbstractRdbEngine { +class RdbEngineOracle extends AbstractRdbEngine { private static final Logger logger = LoggerFactory.getLogger(RdbEngineOracle.class); private final String keyColumnSize; private final RdbEngineTimeTypeOracle timeTypeEngine; @@ -52,13 +52,18 @@ public class RdbEngineOracle extends AbstractRdbEngine { } @Override - public String[] createNamespaceSqls(String fullNamespace) { + public String[] createSchemaSqls(String fullSchema) { return new String[] { - "CREATE USER " + fullNamespace + " IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER " + fullNamespace + " quota unlimited on USERS", + "CREATE USER " + enclose(fullSchema) + " IDENTIFIED BY \"Oracle1234!@#$\"", + "ALTER USER " + enclose(fullSchema) + " quota unlimited on USERS", }; } + @Override + public String[] createSchemaIfNotExistsSqls(String schema) { + return createSchemaSqls(schema); + } + @Override public String createTableInternalPrimaryKeyClause( boolean hasDescClusteringOrder, TableMetadata metadata) { @@ -72,7 +77,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { ArrayList sqls = new ArrayList<>(); // Set INITRANS to 3 and MAXTRANS to 255 for the table to improve the @@ -105,25 +114,12 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql; } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - return new String[] { - "CREATE USER " + enclose(metadataSchema) + " IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER " + enclose(metadataSchema) + " quota unlimited on USERS", - }; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { // ORA-01920: user name 'string' conflicts with another user or role name return e.getErrorCode() == 1920; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - return "DROP USER " + enclose(metadataSchema); - } - @Override public String dropNamespaceSql(String namespace) { return "DROP USER " + enclose(namespace); @@ -242,6 +238,14 @@ public boolean isConflict(SQLException e) { return e.getErrorCode() == 8177 || e.getErrorCode() == 60; } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // https://docs.oracle.com/en/error-help/db/ora-00955/ + // code : 955 + // message : name is already used by an existing object + return e.getErrorCode() == 955; + } + @Override public String getDataTypeForEngine(DataType scalarDbDataType) { switch (scalarDbDataType) { @@ -438,6 +442,11 @@ public String getEscape(LikeExpression likeExpression) { return escape.isEmpty() ? null : escape; } + @Override + public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { + return createIndexSql; + } + @Override @Nullable public String getDataTypeForSecondaryIndex(DataType dataType) { diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java index 6b79dcddde..aa1494e08f 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEnginePostgresql.java @@ -34,8 +34,13 @@ public RdbEnginePostgresql() { } @Override - public String[] createNamespaceSqls(String fullNamespace) { - return new String[] {"CREATE SCHEMA " + fullNamespace}; + public String[] createSchemaSqls(String fullSchema) { + return new String[] {"CREATE SCHEMA " + enclose(fullSchema)}; + } + + @Override + public String[] createSchemaIfNotExistsSqls(String fullSchema) { + return new String[] {"CREATE SCHEMA IF NOT EXISTS " + enclose(fullSchema)}; } @Override @@ -51,7 +56,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { ArrayList sqls = new ArrayList<>(); if (hasDifferentClusteringOrders) { @@ -60,6 +69,7 @@ public String[] createTableInternalSqlsAfterCreateTable( // can be used. sqls.add( "CREATE UNIQUE INDEX " + + (ifNotExists ? "IF NOT EXISTS " : "") + enclose(getFullTableName(schema, table) + "_clustering_order_idx") + " ON " + encloseFullTableName(schema, table) @@ -80,21 +90,11 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS"); } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - return new String[] {"CREATE SCHEMA IF NOT EXISTS " + enclose(metadataSchema)}; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { return false; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - return "DROP SCHEMA " + enclose(metadataSchema); - } - @Override public String dropNamespaceSql(String namespace) { return "DROP SCHEMA " + enclose(namespace); @@ -196,6 +196,12 @@ public boolean isConflict(SQLException e) { return e.getSQLState().equals("40001") || e.getSQLState().equals("40P01"); } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // Since the "IF NOT EXISTS" syntax is used to create an index, we always return false + return false; + } + @Override public String enclose(String name) { return "\"" + name + "\""; @@ -383,6 +389,11 @@ public Driver getDriver() { return new org.postgresql.Driver(); } + @Override + public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { + return createIndexSql.replace("CREATE INDEX", "CREATE INDEX IF NOT EXISTS"); + } + @Override public RdbEngineTimeTypeStrategy getTimeTypeStrategy() { diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java index e528cfbf68..83430e643d 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlServer.java @@ -32,8 +32,13 @@ class RdbEngineSqlServer extends AbstractRdbEngine { } @Override - public String[] createNamespaceSqls(String fullNamespace) { - return new String[] {"CREATE SCHEMA " + fullNamespace}; + public String[] createSchemaSqls(String fullSchema) { + return new String[] {"CREATE SCHEMA " + enclose(fullSchema)}; + } + + @Override + public String[] createSchemaIfNotExistsSqls(String fullSchema) { + return createSchemaSqls(fullSchema); } @Override @@ -60,7 +65,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { // do nothing return new String[0]; } @@ -70,22 +79,12 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql; } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - return new String[] {"CREATE SCHEMA " + enclose(metadataSchema)}; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { // 2714: There is already an object named '%.*ls' in the database. return e.getErrorCode() == 2714; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - return "DROP SCHEMA " + enclose(metadataSchema); - } - @Override public String dropNamespaceSql(String namespace) { return "DROP SCHEMA " + enclose(namespace); @@ -196,6 +195,15 @@ public boolean isConflict(SQLException e) { return e.getErrorCode() == 1205; } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // https://learn.microsoft.com/en-us/sql/relational-databases/errors-events/database-engine-events-and-errors-1000-to-1999?view=sql-server-ver16 + // Error code: 1913 + // Message: The operation failed because an index or statistics with name '%.*ls' already exists + // on %S_MSG '%.*ls'. + return e.getErrorCode() == 1913; + } + @Override public String enclose(String name) { return "[" + name + "]"; @@ -410,6 +418,7 @@ public String getEscape(LikeExpression likeExpression) { return escape.isEmpty() ? "\\" : escape; } + @Override public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { return createIndexSql; } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java index 2331d274db..05224e10ed 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineSqlite.java @@ -38,7 +38,7 @@ * native SQLite library in JAR, we should assure the real error messages in * RdbEngineStrategyTest. */ -public class RdbEngineSqlite extends AbstractRdbEngine { +class RdbEngineSqlite extends AbstractRdbEngine { private static final String NAMESPACE_SEPARATOR = "$"; private final RdbEngineTimeTypeSqlite timeTypeEngine; @@ -86,6 +86,12 @@ public boolean isConflict(SQLException e) { return e.getErrorCode() == 5 || e.getErrorCode() == 6; } + @Override + public boolean isDuplicateIndexError(SQLException e) { + // Since the "IF NOT EXISTS" syntax is used to create an index, we always return false + return false; + } + @Override public String getDataTypeForEngine(DataType scalarDbDataType) { switch (scalarDbDataType) { @@ -182,12 +188,18 @@ public String computeBooleanValue(boolean value) { } @Override - public String[] createNamespaceSqls(String fullNamespace) { - // In SQLite storage, namespace will be added to table names as prefix along with underscore + public String[] createSchemaSqls(String fullSchema) { + // Do nothing, In SQLite storage, namespace will be added to table names as prefix along with + // underscore // separator. return new String[0]; } + @Override + public String[] createSchemaIfNotExistsSqls(String fullSchema) { + return createSchemaSqls(fullSchema); + } + /** * @param hasDescClusteringOrder Ignored. SQLite cannot handle key order. * @see table-constraint @@ -205,7 +217,11 @@ public String createTableInternalPrimaryKeyClause( @Override public String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata) { + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists) { // do nothing return new String[] {}; } @@ -215,25 +231,12 @@ public String tryAddIfNotExistsToCreateTableSql(String createTableSql) { return createTableSql.replace("CREATE TABLE", "CREATE TABLE IF NOT EXISTS"); } - @Override - public String[] createMetadataSchemaIfNotExistsSql(String metadataSchema) { - // Do nothing. Namespace is just a table prefix in the SQLite implementation. - return new String[0]; - } - @Override public boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e) { // Namespace is never created return false; } - @Override - public String deleteMetadataSchemaSql(String metadataSchema) { - // Do nothing. Metadata schema is just a prefix to the metadata table in the SQLite - // implementation. - return null; - } - @Override public String dropNamespaceSql(String namespace) { // Do nothing. Namespace is just a table prefix in the SQLite implementation. @@ -336,6 +339,11 @@ public String getEscape(LikeExpression likeExpression) { return escape.isEmpty() ? null : escape; } + @Override + public String tryAddIfNotExistsToCreateIndexSql(String createIndexSql) { + return createIndexSql.replace("CREATE INDEX", "CREATE INDEX IF NOT EXISTS"); + } + @Override public DateColumn parseDateColumn(ResultSet resultSet, String columnName) throws SQLException { return DateColumn.of( diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java index a3369e104b..16e73c57ec 100644 --- a/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java +++ b/core/src/main/java/com/scalar/db/storage/jdbc/RdbEngineStrategy.java @@ -69,7 +69,9 @@ DataType getDataTypeForScalarDb( String computeBooleanValue(boolean value); - String[] createNamespaceSqls(String fullNamespace); + String[] createSchemaSqls(String fullSchema); + + String[] createSchemaIfNotExistsSqls(String fullSchema); default void throwIfInvalidNamespaceName(String namespaceName) {} @@ -79,16 +81,16 @@ String createTableInternalPrimaryKeyClause( boolean hasDescClusteringOrder, TableMetadata metadata); String[] createTableInternalSqlsAfterCreateTable( - boolean hasDifferentClusteringOrders, String schema, String table, TableMetadata metadata); + boolean hasDifferentClusteringOrders, + String schema, + String table, + TableMetadata metadata, + boolean ifNotExists); String tryAddIfNotExistsToCreateTableSql(String createTableSql); - String[] createMetadataSchemaIfNotExistsSql(String metadataSchema); - boolean isCreateMetadataSchemaDuplicateSchemaError(SQLException e); - String deleteMetadataSchemaSql(String metadataSchema); - String dropNamespaceSql(String namespace); default String truncateTableSql(String namespace, String table) { @@ -190,6 +192,10 @@ default String getPattern(LikeExpression likeExpression) { return likeExpression.getEscape(); } + boolean isDuplicateIndexError(SQLException e); + + String tryAddIfNotExistsToCreateIndexSql(String createIndexSql); + default @Nullable String getCatalogName(String namespace) { return null; } @@ -310,8 +316,6 @@ default void setConnectionToReadOnly(Connection connection, boolean readOnly) default void throwIfCrossPartitionScanOrderingOnBlobColumnNotSupported( ScanAll scanAll, TableMetadata metadata) {} - String getTableNamesInNamespaceSql(); - /** * Throws an exception if one of the conjunctions column is not supported in the underlying * storage. @@ -323,4 +327,6 @@ default void throwIfCrossPartitionScanOrderingOnBlobColumnNotSupported( */ default void throwIfConjunctionsColumnNotSupported( Set conjunctions, TableMetadata metadata) {} + + String getTableNamesInNamespaceSql(); } diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/TableMetadataService.java b/core/src/main/java/com/scalar/db/storage/jdbc/TableMetadataService.java new file mode 100644 index 0000000000..234d9a2d74 --- /dev/null +++ b/core/src/main/java/com/scalar/db/storage/jdbc/TableMetadataService.java @@ -0,0 +1,420 @@ +package com.scalar.db.storage.jdbc; + +import static com.scalar.db.storage.jdbc.JdbcAdmin.execute; +import static com.scalar.db.util.ScalarDbUtils.getFullTableName; + +import com.google.common.annotations.VisibleForTesting; +import com.scalar.db.api.Scan; +import com.scalar.db.api.TableMetadata; +import com.scalar.db.io.DataType; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; +import javax.annotation.Nullable; + +@SuppressFBWarnings({"OBL_UNSATISFIED_OBLIGATION", "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE"}) +public class TableMetadataService { + @VisibleForTesting public static final String TABLE_NAME = "metadata"; + @VisibleForTesting public static final String COL_FULL_TABLE_NAME = "full_table_name"; + @VisibleForTesting static final String COL_COLUMN_NAME = "column_name"; + @VisibleForTesting static final String COL_DATA_TYPE = "data_type"; + @VisibleForTesting static final String COL_KEY_TYPE = "key_type"; + @VisibleForTesting static final String COL_CLUSTERING_ORDER = "clustering_order"; + @VisibleForTesting static final String COL_INDEXED = "indexed"; + @VisibleForTesting static final String COL_ORDINAL_POSITION = "ordinal_position"; + + private final String metadataSchema; + private final RdbEngineStrategy rdbEngine; + + TableMetadataService(String metadataSchema, RdbEngineStrategy rdbEngine) { + this.metadataSchema = metadataSchema; + this.rdbEngine = rdbEngine; + } + + void addTableMetadata( + Connection connection, + String namespace, + String table, + TableMetadata metadata, + boolean createMetadataTable, + boolean overwriteMetadata) + throws SQLException { + if (createMetadataTable) { + createTableMetadataTableIfNotExists(connection); + } + if (overwriteMetadata) { + // Delete the metadata for the table before we add them + execute(connection, getDeleteTableMetadataStatement(namespace, table)); + } + LinkedHashSet orderedColumns = new LinkedHashSet<>(metadata.getPartitionKeyNames()); + orderedColumns.addAll(metadata.getClusteringKeyNames()); + orderedColumns.addAll(metadata.getColumnNames()); + int ordinalPosition = 1; + for (String column : orderedColumns) { + insertMetadataColumn(namespace, table, metadata, connection, ordinalPosition++, column); + } + } + + @VisibleForTesting + void createTableMetadataTableIfNotExists(Connection connection) throws SQLException { + createSchemaIfNotExists(connection, metadataSchema); + + String createTableStatement = + "CREATE TABLE " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + "(" + + enclose(COL_FULL_TABLE_NAME) + + " " + + getTextType(128, true) + + "," + + enclose(COL_COLUMN_NAME) + + " " + + getTextType(128, true) + + "," + + enclose(COL_DATA_TYPE) + + " " + + getTextType(20, false) + + " NOT NULL," + + enclose(COL_KEY_TYPE) + + " " + + getTextType(20, false) + + "," + + enclose(COL_CLUSTERING_ORDER) + + " " + + getTextType(10, false) + + "," + + enclose(COL_INDEXED) + + " " + + getBooleanType() + + " NOT NULL," + + enclose(COL_ORDINAL_POSITION) + + " INTEGER NOT NULL," + + "PRIMARY KEY (" + + enclose(COL_FULL_TABLE_NAME) + + ", " + + enclose(COL_COLUMN_NAME) + + "))"; + + createTable(connection, createTableStatement, true); + } + + private void insertMetadataColumn( + String schema, + String table, + TableMetadata metadata, + Connection connection, + int ordinalPosition, + String column) + throws SQLException { + KeyType keyType = null; + if (metadata.getPartitionKeyNames().contains(column)) { + keyType = KeyType.PARTITION; + } + if (metadata.getClusteringKeyNames().contains(column)) { + keyType = KeyType.CLUSTERING; + } + + String insertStatement = + getInsertStatement( + schema, + table, + column, + metadata.getColumnDataType(column), + keyType, + metadata.getClusteringOrder(column), + metadata.getSecondaryIndexNames().contains(column), + ordinalPosition); + execute(connection, insertStatement); + } + + private String getInsertStatement( + String schema, + String table, + String columnName, + DataType dataType, + @Nullable KeyType keyType, + @Nullable Scan.Ordering.Order ckOrder, + boolean indexed, + int ordinalPosition) { + + return String.format( + "INSERT INTO %s VALUES ('%s','%s','%s',%s,%s,%s,%d)", + encloseFullTableName(metadataSchema, TABLE_NAME), + getFullTableName(schema, table), + columnName, + dataType.toString(), + keyType != null ? "'" + keyType + "'" : "NULL", + ckOrder != null ? "'" + ckOrder + "'" : "NULL", + computeBooleanValue(indexed), + ordinalPosition); + } + + void deleteTableMetadata( + Connection connection, String namespace, String table, boolean deleteMetadataTableIfEmpty) + throws SQLException { + try { + execute(connection, getDeleteTableMetadataStatement(namespace, table)); + if (deleteMetadataTableIfEmpty) { + deleteMetadataTableIfEmpty(connection); + } + } catch (SQLException e) { + if (e.getMessage().contains("Unknown table") || e.getMessage().contains("does not exist")) { + return; + } + throw e; + } + } + + private String getDeleteTableMetadataStatement(String schema, String table) { + return "DELETE FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " WHERE " + + enclose(COL_FULL_TABLE_NAME) + + " = '" + + getFullTableName(schema, table) + + "'"; + } + + private void deleteMetadataTableIfEmpty(Connection connection) throws SQLException { + if (isMetadataTableEmpty(connection)) { + deleteTable(connection, encloseFullTableName(metadataSchema, TABLE_NAME)); + } + } + + private boolean isMetadataTableEmpty(Connection connection) throws SQLException { + String selectAllTables = + "SELECT DISTINCT " + + enclose(COL_FULL_TABLE_NAME) + + " FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME); + try (Statement statement = connection.createStatement(); + ResultSet results = statement.executeQuery(selectAllTables)) { + return !results.next(); + } + } + + TableMetadata getTableMetadata(Connection connection, String namespace, String table) + throws SQLException { + TableMetadata.Builder builder = TableMetadata.newBuilder(); + boolean tableExists = false; + + try { + try (PreparedStatement preparedStatement = + connection.prepareStatement(getSelectColumnsStatement())) { + preparedStatement.setString(1, getFullTableName(namespace, table)); + + try (ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + tableExists = true; + + String columnName = resultSet.getString(COL_COLUMN_NAME); + DataType dataType = DataType.valueOf(resultSet.getString(COL_DATA_TYPE)); + builder.addColumn(columnName, dataType); + + boolean indexed = resultSet.getBoolean(COL_INDEXED); + if (indexed) { + builder.addSecondaryIndex(columnName); + } + + String keyType = resultSet.getString(COL_KEY_TYPE); + if (keyType == null) { + continue; + } + + switch (KeyType.valueOf(keyType)) { + case PARTITION: + builder.addPartitionKey(columnName); + break; + case CLUSTERING: + Scan.Ordering.Order clusteringOrder = + Scan.Ordering.Order.valueOf(resultSet.getString(COL_CLUSTERING_ORDER)); + builder.addClusteringKey(columnName, clusteringOrder); + break; + default: + throw new AssertionError("Invalid key type: " + keyType); + } + } + } + } + } catch (SQLException e) { + // An exception will be thrown if the namespace table does not exist when executing the select + // query + if (rdbEngine.isUndefinedTableError(e)) { + return null; + } + throw e; + } + + if (!tableExists) { + return null; + } + + return builder.build(); + } + + private String getSelectColumnsStatement() { + return "SELECT " + + enclose(COL_COLUMN_NAME) + + "," + + enclose(COL_DATA_TYPE) + + "," + + enclose(COL_KEY_TYPE) + + "," + + enclose(COL_CLUSTERING_ORDER) + + "," + + enclose(COL_INDEXED) + + " FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " WHERE " + + enclose(COL_FULL_TABLE_NAME) + + "=? ORDER BY " + + enclose(COL_ORDINAL_POSITION) + + " ASC"; + } + + void updateTableMetadata( + Connection connection, String schema, String table, String columnName, boolean indexed) + throws SQLException { + String updateStatement = + "UPDATE " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " SET " + + enclose(COL_INDEXED) + + "=" + + computeBooleanValue(indexed) + + " WHERE " + + enclose(COL_FULL_TABLE_NAME) + + "='" + + getFullTableName(schema, table) + + "' AND " + + enclose(COL_COLUMN_NAME) + + "='" + + columnName + + "'"; + execute(connection, updateStatement); + } + + Set getNamespaceTableNames(Connection connection, String namespace) throws SQLException { + String selectTablesOfNamespaceStatement = + "SELECT DISTINCT " + + enclose(COL_FULL_TABLE_NAME) + + " FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " WHERE " + + enclose(COL_FULL_TABLE_NAME) + + " LIKE ?"; + try { + try (PreparedStatement preparedStatement = + connection.prepareStatement(selectTablesOfNamespaceStatement)) { + String prefix = namespace + "."; + preparedStatement.setString(1, prefix + "%"); + try (ResultSet results = preparedStatement.executeQuery()) { + Set tableNames = new HashSet<>(); + while (results.next()) { + String tableName = results.getString(COL_FULL_TABLE_NAME).substring(prefix.length()); + tableNames.add(tableName); + } + return tableNames; + } + } + + } catch (SQLException e) { + // An exception will be thrown if the metadata table does not exist when executing the select + // query + if (rdbEngine.isUndefinedTableError(e)) { + return Collections.emptySet(); + } + throw e; + } + } + + Set getNamespaceNames(Connection connection) throws SQLException { + String selectAllTableNames = + "SELECT DISTINCT " + + enclose(COL_FULL_TABLE_NAME) + + " FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME); + try { + try (Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery(selectAllTableNames)) { + Set namespaceOfExistingTables = new HashSet<>(); + while (rs.next()) { + String fullTableName = rs.getString(COL_FULL_TABLE_NAME); + String namespaceName = fullTableName.substring(0, fullTableName.indexOf('.')); + namespaceOfExistingTables.add(namespaceName); + } + namespaceOfExistingTables.add(metadataSchema); + + return namespaceOfExistingTables; + } + } catch (SQLException e) { + // An exception will be thrown if the namespace table does not exist when executing the select + // query + if (rdbEngine.isUndefinedTableError(e)) { + return Collections.singleton(metadataSchema); + } + throw e; + } + } + + private String computeBooleanValue(boolean value) { + return rdbEngine.computeBooleanValue(value); + } + + private String getBooleanType() { + return rdbEngine.getDataTypeForEngine(DataType.BOOLEAN); + } + + private String getTextType(int charLength, boolean isKey) { + return rdbEngine.getTextType(charLength, isKey); + } + + private void createTable(Connection connection, String createTableStatement, boolean ifNotExists) + throws SQLException { + String stmt = createTableStatement; + if (ifNotExists) { + stmt = rdbEngine.tryAddIfNotExistsToCreateTableSql(createTableStatement); + } + try { + execute(connection, stmt); + } catch (SQLException e) { + // Suppress the exception thrown when the table already exists + if (!(ifNotExists && rdbEngine.isDuplicateTableError(e))) { + throw e; + } + } + } + + private void deleteTable(Connection connection, String fullTableName) throws SQLException { + String dropTableStatement = "DROP TABLE " + fullTableName; + + execute(connection, dropTableStatement); + } + + private void createSchemaIfNotExists(Connection connection, String schema) throws SQLException { + String[] sqls = rdbEngine.createSchemaIfNotExistsSqls(schema); + try { + execute(connection, sqls); + } catch (SQLException e) { + // Suppress exceptions indicating the duplicate metadata schema + if (!rdbEngine.isCreateMetadataSchemaDuplicateSchemaError(e)) { + throw e; + } + } + } + + private String enclose(String name) { + return rdbEngine.enclose(name); + } + + private String encloseFullTableName(String schema, String table) { + return rdbEngine.encloseFullTableName(schema, table); + } +} diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTestBase.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java similarity index 70% rename from core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTestBase.java rename to core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java index c831713e5e..3c0f5eb4a8 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTestBase.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java @@ -29,7 +29,6 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.mysql.cj.jdbc.exceptions.CommunicationsException; @@ -38,7 +37,6 @@ import com.scalar.db.common.CoreError; import com.scalar.db.exception.storage.ExecutionException; import com.scalar.db.io.DataType; -import com.scalar.db.storage.jdbc.JdbcAdminTestBase.GetColumnsResultSetMocker.Row; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.JDBCType; @@ -73,15 +71,11 @@ import org.sqlite.SQLiteErrorCode; import org.sqlite.SQLiteException; -/** - * Abstraction that defines unit tests for the {@link JdbcAdmin}. The class purpose is to be able to - * run the {@link JdbcAdmin} unit tests with different values for the {@link JdbcConfig}, notably - * {@link JdbcConfig#TABLE_METADATA_SCHEMA}. - */ -public abstract class JdbcAdminTestBase { +public class JdbcAdminTest { + + private static final String METADATA_SCHEMA = "scalardb"; private static final String NAMESPACE = "namespace"; private static final String TABLE = "table"; - private static final String COLUMN_1 = "c1"; private static final ImmutableMap RDB_ENGINES = ImmutableMap.of( RdbEngine.MYSQL, @@ -105,16 +99,12 @@ public abstract class JdbcAdminTestBase { @Mock private Connection connection; @Mock private JdbcConfig config; - private String tableMetadataSchemaName; - @BeforeEach public void setUp() throws Exception { MockitoAnnotations.openMocks(this).close(); // Arrange - when(config.getTableMetadataSchema()).thenReturn(getTableMetadataSchemaConfig()); - - tableMetadataSchemaName = getTableMetadataSchemaConfig().orElse("scalardb"); + when(config.getTableMetadataSchema()).thenReturn(Optional.of(METADATA_SCHEMA)); } private JdbcAdmin createJdbcAdminFor(RdbEngine rdbEngine) { @@ -164,21 +154,13 @@ private void mockUndefinedTableError(RdbEngine rdbEngine, SQLException sqlExcept } } - /** - * This sets the {@link JdbcConfig#TABLE_METADATA_SCHEMA} value that will be used to run the - * tests. - * - * @return {@link JdbcConfig#TABLE_METADATA_SCHEMA} value - */ - abstract Optional getTableMetadataSchemaConfig(); - @Test public void getTableMetadata_forMysql_ShouldReturnTableMetadata() throws SQLException, ExecutionException { getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC"); } @@ -188,7 +170,7 @@ public void getTableMetadata_forPostgresql_ShouldReturnTableMetadata() getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC"); } @@ -198,7 +180,7 @@ public void getTableMetadata_forSqlServer_ShouldReturnTableMetadata() getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC"); } @@ -208,7 +190,7 @@ public void getTableMetadata_forOracle_ShouldReturnTableMetadata() getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC"); } @@ -218,7 +200,7 @@ public void getTableMetadata_forSqlite_ShouldReturnTableMetadata() getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.SQLITE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC"); } @@ -228,7 +210,7 @@ public void getTableMetadata_forDb2_ShouldReturnTableMetadata() getTableMetadata_forX_ShouldReturnTableMetadata( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC"); } @@ -239,34 +221,33 @@ private void getTableMetadata_forX_ShouldReturnTableMetadata( String namespace = "ns"; String table = "table"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "c3", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - "c1", DataType.TEXT.toString(), "CLUSTERING", Order.DESC.toString(), false), - new GetColumnsResultSetMocker.Row( - "c4", DataType.BLOB.toString(), "CLUSTERING", Order.ASC.toString(), true), - new GetColumnsResultSetMocker.Row( - "c2", DataType.BIGINT.toString(), null, null, false), - new GetColumnsResultSetMocker.Row("c5", DataType.INT.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c6", DataType.DOUBLE.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c7", DataType.FLOAT.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c8", DataType.DATE.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c9", DataType.TIME.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c10", DataType.TIMESTAMP.toString(), null, null, false), - new GetColumnsResultSetMocker.Row( - "c11", DataType.TIMESTAMPTZ.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + "c3", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c1", DataType.TEXT.toString(), "CLUSTERING", Order.DESC.toString(), false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c4", DataType.BLOB.toString(), "CLUSTERING", Order.ASC.toString(), true), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c2", DataType.BIGINT.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c5", DataType.INT.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c6", DataType.DOUBLE.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c7", DataType.FLOAT.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c8", DataType.DATE.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c9", DataType.TIME.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c10", DataType.TIMESTAMP.toString(), null, null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + "c11", DataType.TIMESTAMPTZ.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); when(dataSource.getConnection()).thenReturn(connection); JdbcAdmin admin = createJdbcAdminFor(rdbEngine); @@ -302,18 +283,21 @@ private void getTableMetadata_forX_ShouldReturnTableMetadata( verify(connection).prepareStatement(expectedSelectStatements); } - public ResultSet mockResultSet(List rows) throws SQLException { + public ResultSet mockResultSet(SelectAllFromMetadataTableResultSetMocker.Row... rows) + throws SQLException { ResultSet resultSet = mock(ResultSet.class); - // Everytime the ResultSet.next() method will be called, the ResultSet.getXXX methods call is + // Everytime the ResultSet.next() method will be called, the ResultSet.getXXX methods call be // mocked to return the current row data - doAnswer(new GetColumnsResultSetMocker(rows)).when(resultSet).next(); + doAnswer(new SelectAllFromMetadataTableResultSetMocker(Arrays.asList(rows))) + .when(resultSet) + .next(); return resultSet; } public ResultSet mockResultSet(SelectFullTableNameFromMetadataTableResultSetMocker.Row... rows) throws SQLException { ResultSet resultSet = mock(ResultSet.class); - // Everytime the ResultSet.next() method will be called, the ResultSet.getXXX methods call is + // Everytime the ResultSet.next() method will be called, the ResultSet.getXXX methods call be // mocked to return the current row data doAnswer(new SelectFullTableNameFromMetadataTableResultSetMocker(Arrays.asList(rows))) .when(resultSet) @@ -325,20 +309,23 @@ public ResultSet mockResultSet(SelectFullTableNameFromMetadataTableResultSetMock public void getTableMetadata_MetadataSchemaNotExistsForX_ShouldReturnNull() throws SQLException, ExecutionException { for (RdbEngine rdbEngine : RDB_ENGINES.keySet()) { - getTableMetadata_MetadataSchemaNotExistsForX_ShouldReturnNull(rdbEngine); + getTableMetadata_MetadataTableNotExistsForX_ShouldReturnNull(rdbEngine); } } - private void getTableMetadata_MetadataSchemaNotExistsForX_ShouldReturnNull(RdbEngine rdbEngine) + private void getTableMetadata_MetadataTableNotExistsForX_ShouldReturnNull(RdbEngine rdbEngine) throws SQLException, ExecutionException { // Arrange JdbcAdmin admin = createJdbcAdminFor(rdbEngine); Connection connection = mock(Connection.class); - PreparedStatement selectStatement = prepareStatementForNamespaceCheck(false); + PreparedStatement selectStatement = mock(PreparedStatement.class); when(dataSource.getConnection()).thenReturn(connection); when(connection.prepareStatement(any())).thenReturn(selectStatement); + SQLException sqlException = mock(SQLException.class); + mockUndefinedTableError(rdbEngine, sqlException); + when(selectStatement.executeQuery()).thenThrow(sqlException); // Act TableMetadata actual = admin.getTableMetadata("my_ns", "my_tbl"); @@ -416,7 +403,20 @@ private void createNamespace_forX_shouldExecuteCreateNamespaceStatement( } @Test - public void createTable_forSqlite_withInvalidTableName_shouldThrowIllegalArgumentException() { + public void createNamespace_ForSqlite_withInvalidNamespaceName_ShouldThrowExecutionException() { + // Arrange + String namespace = "my$ns"; // contains namespace separator + + JdbcAdmin admin = createJdbcAdminFor(RdbEngine.SQLITE); + + // Act + // Assert + assertThatThrownBy(() -> admin.createNamespace(namespace)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + public void createTableInternal_ForSqlite_withInvalidTableName_ShouldThrowExecutionException() { // Arrange String namespace = "my_ns"; String table = "foo$table"; // contains namespace separator @@ -427,405 +427,126 @@ public void createTable_forSqlite_withInvalidTableName_shouldThrowIllegalArgumen // Act // Assert - assertThatThrownBy(() -> admin.createTable(namespace, table, metadata, new HashMap<>())) + assertThatThrownBy( + () -> + admin.createTableInternal( + mock(Connection.class), namespace, table, metadata, false)) .isInstanceOf(IllegalArgumentException.class); } @Test - public void createTable_forMysql_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( + public void createTableInternal_ForMysql_ShouldCreateTableAndIndexes() throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( RdbEngine.MYSQL, - "CREATE SCHEMA IF NOT EXISTS `" + tableMetadataSchemaName + "`", - "CREATE TABLE IF NOT EXISTS `" - + tableMetadataSchemaName - + "`.`metadata`(" - + "`full_table_name` VARCHAR(128)," - + "`column_name` VARCHAR(128)," - + "`data_type` VARCHAR(20) NOT NULL," - + "`key_type` VARCHAR(20)," - + "`clustering_order` VARCHAR(10)," - + "`indexed` BOOLEAN NOT NULL," - + "`ordinal_position` INTEGER NOT NULL," - + "PRIMARY KEY (`full_table_name`, `column_name`))", - "CREATE TABLE `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(128),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL, PRIMARY KEY (`c3`,`c1`,`c5`))", + "CREATE TABLE `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(128),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL,`c8` DATE,`c9` TIME(6),`c10` DATETIME(3),`c11` DATETIME(3), PRIMARY KEY (`c3` ASC,`c1` DESC,`c5` ASC))", "CREATE INDEX `index_my_ns_foo_table_c5` ON `my_ns`.`foo_table` (`c5`)", - "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',true,2)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)"); + "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)"); } @Test - public void createTable_forMysqlWithModifiedKeyColumnSize_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { + public void + createTableInternal_ForMysqlWithModifiedKeyColumnSize_ShouldCreateTableAndIndexesWithModifiedKeyColumnSize() + throws SQLException { when(config.getMysqlVariableKeyColumnSize()).thenReturn(64); - createTable_forX_shouldExecuteCreateTableStatement( + createTableInternal_ForX_CreateTableAndIndexes( new RdbEngineMysql(config), - "CREATE SCHEMA IF NOT EXISTS `" + tableMetadataSchemaName + "`", - "CREATE TABLE IF NOT EXISTS `" - + tableMetadataSchemaName - + "`.`metadata`(" - + "`full_table_name` VARCHAR(128)," - + "`column_name` VARCHAR(128)," - + "`data_type` VARCHAR(20) NOT NULL," - + "`key_type` VARCHAR(20)," - + "`clustering_order` VARCHAR(10)," - + "`indexed` BOOLEAN NOT NULL," - + "`ordinal_position` INTEGER NOT NULL," - + "PRIMARY KEY (`full_table_name`, `column_name`))", - "CREATE TABLE `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(64),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL, PRIMARY KEY (`c3`,`c1`,`c5`))", + "CREATE TABLE `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(64),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL,`c8` DATE,`c9` TIME(6),`c10` DATETIME(3),`c11` DATETIME(3), PRIMARY KEY (`c3` ASC,`c1` DESC,`c5` ASC))", "CREATE INDEX `index_my_ns_foo_table_c5` ON `my_ns`.`foo_table` (`c5`)", - "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',true,2)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)"); + "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)"); } @Test - public void createTable_forPostgresql_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( + public void createTableInternal_ForPostgresql_ShouldCreateTableAndIndexes() throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( RdbEngine.POSTGRESQL, - "CREATE SCHEMA IF NOT EXISTS \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(" - + "\"full_table_name\" VARCHAR(128)," - + "\"column_name\" VARCHAR(128)," - + "\"data_type\" VARCHAR(20) NOT NULL," - + "\"key_type\" VARCHAR(20)," - + "\"clustering_order\" VARCHAR(10)," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN,\"c1\" VARCHAR(10485760),\"c5\" INT,\"c2\" BIGINT,\"c4\" BYTEA,\"c6\" DOUBLE PRECISION,\"c7\" REAL, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN,\"c1\" VARCHAR(10485760),\"c5\" INT,\"c2\" BIGINT,\"c4\" BYTEA,\"c6\" DOUBLE PRECISION,\"c7\" REAL,\"c8\" DATE,\"c9\" TIME,\"c10\" TIMESTAMP,\"c11\" TIMESTAMP WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',true,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)"); + "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_forSqlServer_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( + public void createTableInternal_ForSqlServer_ShouldCreateTableAndIndexes() throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( RdbEngine.SQL_SERVER, - "CREATE SCHEMA [" + tableMetadataSchemaName + "]", - "CREATE TABLE [" - + tableMetadataSchemaName - + "].[metadata](" - + "[full_table_name] VARCHAR(128)," - + "[column_name] VARCHAR(128)," - + "[data_type] VARCHAR(20) NOT NULL," - + "[key_type] VARCHAR(20)," - + "[clustering_order] VARCHAR(10)," - + "[indexed] BIT NOT NULL," - + "[ordinal_position] INTEGER NOT NULL," - + "PRIMARY KEY ([full_table_name], [column_name]))", - "CREATE TABLE [my_ns].[foo_table]([c3] BIT,[c1] VARCHAR(8000),[c5] INT,[c2] BIGINT,[c4] VARBINARY(8000),[c6] FLOAT,[c7] FLOAT(24), PRIMARY KEY ([c3],[c1],[c5]))", + "CREATE TABLE [my_ns].[foo_table]([c3] BIT,[c1] VARCHAR(8000),[c5] INT,[c2] BIGINT,[c4] VARBINARY(8000),[c6] FLOAT,[c7] FLOAT(24),[c8] DATE,[c9] TIME(6),[c10] DATETIME2(3),[c11] DATETIMEOFFSET(3), PRIMARY KEY ([c3] ASC,[c1] DESC,[c5] ASC))", "CREATE INDEX [index_my_ns_foo_table_c5] ON [my_ns].[foo_table] ([c5])", - "CREATE INDEX [index_my_ns_foo_table_c1] ON [my_ns].[foo_table] ([c1])", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',1,2)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',1,3)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,0,5)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)"); + "CREATE INDEX [index_my_ns_foo_table_c1] ON [my_ns].[foo_table] ([c1])"); } @Test - public void createTable_forOracle_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( + public void createTableInternal_ForOracle_ShouldCreateTableAndIndexes() throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( RdbEngine.ORACLE, - "CREATE USER \"" + tableMetadataSchemaName + "\" IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER \"" + tableMetadataSchemaName + "\" quota unlimited on USERS", - "CREATE TABLE \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" NUMBER(1),\"c1\" VARCHAR2(128),\"c5\" NUMBER(10),\"c2\" NUMBER(16),\"c4\" BLOB,\"c6\" BINARY_DOUBLE,\"c7\" BINARY_FLOAT, PRIMARY KEY (\"c3\",\"c1\",\"c5\")) ROWDEPENDENCIES", + "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" NUMBER(1),\"c1\" VARCHAR2(128),\"c5\" NUMBER(10),\"c2\" NUMBER(16),\"c4\" BLOB,\"c6\" BINARY_DOUBLE,\"c7\" BINARY_FLOAT,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3) WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\")) ROWDEPENDENCIES", "ALTER TABLE \"my_ns\".\"foo_table\" INITRANS 3 MAXTRANS 255", + "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',1,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',1,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,0,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_forOracleWithModifiedKeyColumnSize_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { + public void + createTableInternal_ForOracleWithModifiedKeyColumnSize_ShouldCreateTableAndIndexesWithModifiedKeyColumnSize() + throws SQLException { when(config.getOracleVariableKeyColumnSize()).thenReturn(64); - createTable_forX_shouldExecuteCreateTableStatement( + createTableInternal_ForX_CreateTableAndIndexes( new RdbEngineOracle(config), - "CREATE USER \"" + tableMetadataSchemaName + "\" IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER \"" + tableMetadataSchemaName + "\" quota unlimited on USERS", - "CREATE TABLE \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" NUMBER(1),\"c1\" VARCHAR2(64),\"c5\" NUMBER(10),\"c2\" NUMBER(16),\"c4\" BLOB,\"c6\" BINARY_DOUBLE,\"c7\" BINARY_FLOAT, PRIMARY KEY (\"c3\",\"c1\",\"c5\")) ROWDEPENDENCIES", + "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" NUMBER(1),\"c1\" VARCHAR2(64),\"c5\" NUMBER(10),\"c2\" NUMBER(16),\"c4\" BLOB,\"c6\" BINARY_DOUBLE,\"c7\" BINARY_FLOAT,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3) WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\")) ROWDEPENDENCIES", "ALTER TABLE \"my_ns\".\"foo_table\" INITRANS 3 MAXTRANS 255", + "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',1,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',1,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,0,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_forSqlite_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( + public void createTableInternal_ForSqlite_ShouldCreateTableAndIndexes() throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( RdbEngine.SQLITE, - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "$metadata\"(" - + "\"full_table_name\" TEXT," - + "\"column_name\" TEXT," - + "\"data_type\" TEXT NOT NULL," - + "\"key_type\" TEXT," - + "\"clustering_order\" TEXT," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns$foo_table\"(\"c3\" BOOLEAN,\"c1\" TEXT,\"c5\" INT,\"c2\" BIGINT,\"c4\" BLOB,\"c6\" DOUBLE,\"c7\" FLOAT, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE TABLE \"my_ns$foo_table\"(\"c3\" BOOLEAN,\"c1\" TEXT,\"c5\" INT,\"c2\" BIGINT,\"c4\" BLOB,\"c6\" DOUBLE,\"c7\" FLOAT,\"c8\" INT,\"c9\" BIGINT,\"c10\" BIGINT,\"c11\" BIGINT, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", "CREATE INDEX \"index_my_ns_foo_table_c5\" ON \"my_ns$foo_table\" (\"c5\")", - "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns$foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,FALSE,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',TRUE,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',TRUE,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,FALSE,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,FALSE,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,FALSE,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,FALSE,7)"); + "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns$foo_table\" (\"c1\")"); } @Test - public void createTable_forDb2_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_forX_shouldExecuteCreateTableStatement( - RdbEngine.DB2, - "CREATE SCHEMA \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(" - + "\"full_table_name\" VARCHAR(128) NOT NULL," - + "\"column_name\" VARCHAR(128) NOT NULL," - + "\"data_type\" VARCHAR(20) NOT NULL," - + "\"key_type\" VARCHAR(20)," - + "\"clustering_order\" VARCHAR(10)," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(128) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + public void createTableInternal_ForDb2_ShouldCreateTableAndIndexes() throws SQLException { + when(config.getDb2VariableKeyColumnSize()).thenReturn(64); + createTableInternal_ForX_CreateTableAndIndexes( + new RdbEngineDb2(config), + "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(64) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3), PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',true,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_forDb2WithModifiedKeyColumnSize_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - when(config.getDb2VariableKeyColumnSize()).thenReturn(64); - createTable_forX_shouldExecuteCreateTableStatement( - new RdbEngineDb2(config), - "CREATE SCHEMA \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(" - + "\"full_table_name\" VARCHAR(128) NOT NULL," - + "\"column_name\" VARCHAR(128) NOT NULL," - + "\"data_type\" VARCHAR(20) NOT NULL," - + "\"key_type\" VARCHAR(20)," - + "\"clustering_order\" VARCHAR(10)," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(64) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + public void + createTableInternal_ForDb2WithModifiedKeyColumnSize_ShouldCreateTableAndIndexesWithModifiedKeyColumnSize() + throws SQLException { + createTableInternal_ForX_CreateTableAndIndexes( + RdbEngine.DB2, + "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(128) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3), PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','ASC',true,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } - private void createTable_forX_shouldExecuteCreateTableStatement( - RdbEngine rdbEngine, String... expectedSqlStatements) - throws SQLException, ExecutionException { + private void createTableInternal_ForX_CreateTableAndIndexes( + RdbEngine rdbEngine, String... expectedSqlStatements) throws SQLException { RdbEngineStrategy rdbEngineStrategy = RdbEngine.createRdbEngineStrategy(rdbEngine); - createTable_forX_shouldExecuteCreateTableStatement(rdbEngineStrategy, expectedSqlStatements); + createTableInternal_ForX_CreateTableAndIndexes(rdbEngineStrategy, expectedSqlStatements); } - private void createTable_forX_shouldExecuteCreateTableStatement( - RdbEngineStrategy rdbEngineStrategy, String... expectedSqlStatements) - throws SQLException, ExecutionException { + private void createTableInternal_ForX_CreateTableAndIndexes( + RdbEngineStrategy rdbEngineStrategy, String... expectedSqlStatements) throws SQLException { // Arrange String namespace = "my_ns"; String table = "foo_table"; TableMetadata metadata = TableMetadata.newBuilder() .addPartitionKey("c3") - .addClusteringKey("c1") - .addClusteringKey("c5") + .addClusteringKey("c1", Order.DESC) + .addClusteringKey("c5", Order.ASC) .addColumn("c1", DataType.TEXT) .addColumn("c2", DataType.BIGINT) .addColumn("c3", DataType.BOOLEAN) @@ -833,6 +554,10 @@ private void createTable_forX_shouldExecuteCreateTableStatement( .addColumn("c5", DataType.INT) .addColumn("c6", DataType.DOUBLE) .addColumn("c7", DataType.FLOAT) + .addColumn("c8", DataType.DATE) + .addColumn("c9", DataType.TIME) + .addColumn("c10", DataType.TIMESTAMP) + .addColumn("c11", DataType.TIMESTAMPTZ) .addSecondaryIndex("c1") .addSecondaryIndex("c5") .build(); @@ -850,7 +575,7 @@ private void createTable_forX_shouldExecuteCreateTableStatement( JdbcAdmin admin = createJdbcAdminFor(rdbEngineStrategy); // Act - admin.createTable(namespace, table, metadata, new HashMap<>()); + admin.createTableInternal(connection, namespace, table, metadata, false); // Assert for (int i = 0; i < expectedSqlStatements.length; i++) { @@ -859,334 +584,78 @@ private void createTable_forX_shouldExecuteCreateTableStatement( } @Test - public void createTable_WithClusteringOrderForMysql_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForMysql_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.MYSQL, - "CREATE SCHEMA IF NOT EXISTS `" + tableMetadataSchemaName + "`", - "CREATE TABLE IF NOT EXISTS `" - + tableMetadataSchemaName - + "`.`metadata`(" - + "`full_table_name` VARCHAR(128)," - + "`column_name` VARCHAR(128)," - + "`data_type` VARCHAR(20) NOT NULL," - + "`key_type` VARCHAR(20)," - + "`clustering_order` VARCHAR(10)," - + "`indexed` BOOLEAN NOT NULL," - + "`ordinal_position` INTEGER NOT NULL," - + "PRIMARY KEY (`full_table_name`, `column_name`))", - "CREATE TABLE `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(128),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL,`c8` DATE,`c9` TIME(6),`c10` DATETIME(3),`c11` DATETIME(3), PRIMARY KEY (`c3` ASC,`c1` DESC,`c5` ASC))", + "CREATE TABLE IF NOT EXISTS `my_ns`.`foo_table`(`c3` BOOLEAN,`c1` VARCHAR(128),`c5` INT,`c2` BIGINT,`c4` LONGBLOB,`c6` DOUBLE,`c7` REAL,`c8` DATE,`c9` TIME(6),`c10` DATETIME(3),`c11` DATETIME(3), PRIMARY KEY (`c3` ASC,`c1` DESC,`c5` ASC))", "CREATE INDEX `index_my_ns_foo_table_c5` ON `my_ns`.`foo_table` (`c5`)", - "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); + "CREATE INDEX `index_my_ns_foo_table_c1` ON `my_ns`.`foo_table` (`c1`)"); } @Test - public void createTable_WithClusteringOrderForPostgresql_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForPostgresql_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.POSTGRESQL, - "CREATE SCHEMA IF NOT EXISTS \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(" - + "\"full_table_name\" VARCHAR(128)," - + "\"column_name\" VARCHAR(128)," - + "\"data_type\" VARCHAR(20) NOT NULL," - + "\"key_type\" VARCHAR(20)," - + "\"clustering_order\" VARCHAR(10)," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN,\"c1\" VARCHAR(10485760),\"c5\" INT,\"c2\" BIGINT,\"c4\" BYTEA,\"c6\" DOUBLE PRECISION,\"c7\" REAL,\"c8\" DATE,\"c9\" TIME,\"c10\" TIMESTAMP,\"c11\" TIMESTAMP WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", - "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", - "CREATE INDEX \"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); + "CREATE TABLE IF NOT EXISTS \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN,\"c1\" VARCHAR(10485760),\"c5\" INT,\"c2\" BIGINT,\"c4\" BYTEA,\"c6\" DOUBLE PRECISION,\"c7\" REAL,\"c8\" DATE,\"c9\" TIME,\"c10\" TIMESTAMP,\"c11\" TIMESTAMP WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE UNIQUE INDEX IF NOT EXISTS \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", + "CREATE INDEX IF NOT EXISTS \"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", + "CREATE INDEX IF NOT EXISTS \"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_WithClusteringOrderForSqlServer_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForSqlServer_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.SQL_SERVER, - "CREATE SCHEMA [" + tableMetadataSchemaName + "]", - "CREATE TABLE [" - + tableMetadataSchemaName - + "].[metadata](" - + "[full_table_name] VARCHAR(128)," - + "[column_name] VARCHAR(128)," - + "[data_type] VARCHAR(20) NOT NULL," - + "[key_type] VARCHAR(20)," - + "[clustering_order] VARCHAR(10)," - + "[indexed] BIT NOT NULL," - + "[ordinal_position] INTEGER NOT NULL," - + "PRIMARY KEY ([full_table_name], [column_name]))", "CREATE TABLE [my_ns].[foo_table]([c3] BIT,[c1] VARCHAR(8000),[c5] INT,[c2] BIGINT,[c4] VARBINARY(8000),[c6] FLOAT,[c7] FLOAT(24),[c8] DATE,[c9] TIME(6),[c10] DATETIME2(3),[c11] DATETIMEOFFSET(3), PRIMARY KEY ([c3] ASC,[c1] DESC,[c5] ASC))", "CREATE INDEX [index_my_ns_foo_table_c5] ON [my_ns].[foo_table] ([c5])", - "CREATE INDEX [index_my_ns_foo_table_c1] ON [my_ns].[foo_table] ([c1])", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',1,2)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',1,3)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,0,5)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,0,8)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,0,9)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,0,10)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,0,11)"); + "CREATE INDEX [index_my_ns_foo_table_c1] ON [my_ns].[foo_table] ([c1])"); } @Test - public void createTable_WithClusteringOrderForOracle_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForOracle_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.ORACLE, - "CREATE USER \"" + tableMetadataSchemaName + "\" IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER \"" + tableMetadataSchemaName + "\" quota unlimited on USERS", - "CREATE TABLE \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" NUMBER(1),\"c1\" VARCHAR2(128),\"c5\" NUMBER(10),\"c2\" NUMBER(16),\"c4\" BLOB,\"c6\" BINARY_DOUBLE,\"c7\" BINARY_FLOAT,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3) WITH TIME ZONE, PRIMARY KEY (\"c3\",\"c1\",\"c5\")) ROWDEPENDENCIES", "ALTER TABLE \"my_ns\".\"foo_table\" INITRANS 3 MAXTRANS 255", "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',1,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',1,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,0,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,0,8)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,0,9)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,0,10)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,0,11)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } @Test - public void createTable_WithClusteringOrderForSqlite_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForSqlite_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.SQLITE, - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "$metadata\"(" - + "\"full_table_name\" TEXT," - + "\"column_name\" TEXT," - + "\"data_type\" TEXT NOT NULL," - + "\"key_type\" TEXT," - + "\"clustering_order\" TEXT," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns$foo_table\"(\"c3\" BOOLEAN,\"c1\" TEXT,\"c5\" INT,\"c2\" BIGINT,\"c4\" BLOB,\"c6\" DOUBLE,\"c7\" FLOAT,\"c8\" INT,\"c9\" BIGINT,\"c10\" BIGINT,\"c11\" BIGINT, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", - "CREATE INDEX \"index_my_ns_foo_table_c5\" ON \"my_ns$foo_table\" (\"c5\")", - "CREATE INDEX \"index_my_ns_foo_table_c1\" ON \"my_ns$foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,FALSE,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',TRUE,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',TRUE,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,FALSE,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,FALSE,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,FALSE,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,FALSE,7)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,FALSE,8)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,FALSE,9)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,FALSE,10)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,FALSE,11)"); + "CREATE TABLE IF NOT EXISTS \"my_ns$foo_table\"(\"c3\" BOOLEAN,\"c1\" TEXT,\"c5\" INT,\"c2\" BIGINT,\"c4\" BLOB,\"c6\" DOUBLE,\"c7\" FLOAT,\"c8\" INT,\"c9\" BIGINT,\"c10\" BIGINT,\"c11\" BIGINT, PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE INDEX IF NOT EXISTS \"index_my_ns_foo_table_c5\" ON \"my_ns$foo_table\" (\"c5\")", + "CREATE INDEX IF NOT EXISTS \"index_my_ns_foo_table_c1\" ON \"my_ns$foo_table\" (\"c1\")"); } @Test - public void createTable_WithClusteringOrderForDb2_shouldExecuteCreateTableStatement() - throws ExecutionException, SQLException { - createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( + public void createTableInternal_IfNotExistsForDb2_ShouldCreateTableAndIndexesIfNotExists() + throws SQLException { + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( RdbEngine.DB2, - "CREATE SCHEMA \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(" - + "\"full_table_name\" VARCHAR(128) NOT NULL," - + "\"column_name\" VARCHAR(128) NOT NULL," - + "\"data_type\" VARCHAR(20) NOT NULL," - + "\"key_type\" VARCHAR(20)," - + "\"clustering_order\" VARCHAR(10)," - + "\"indexed\" BOOLEAN NOT NULL," - + "\"ordinal_position\" INTEGER NOT NULL," - + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "CREATE TABLE \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(128) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3), PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", + "CREATE TABLE IF NOT EXISTS \"my_ns\".\"foo_table\"(\"c3\" BOOLEAN NOT NULL,\"c1\" VARCHAR(128) NOT NULL,\"c5\" INT NOT NULL,\"c2\" BIGINT,\"c4\" BLOB(2G),\"c6\" DOUBLE,\"c7\" REAL,\"c8\" DATE,\"c9\" TIMESTAMP(6),\"c10\" TIMESTAMP(3),\"c11\" TIMESTAMP(3), PRIMARY KEY (\"c3\",\"c1\",\"c5\"))", "CREATE UNIQUE INDEX \"my_ns.foo_table_clustering_order_idx\" ON \"my_ns\".\"foo_table\" (\"c3\" ASC,\"c1\" DESC,\"c5\" ASC)", "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c5\" ON \"my_ns\".\"foo_table\" (\"c5\")", - "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT','CLUSTERING','ASC',true,3)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB',NULL,NULL,false,5)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); + "CREATE INDEX \"my_ns\".\"index_my_ns_foo_table_c1\" ON \"my_ns\".\"foo_table\" (\"c1\")"); } - private void createTable_WithClusteringOrderForX_shouldExecuteCreateTableStatement( - RdbEngine rdbEngine, String... expectedSqlStatements) - throws SQLException, ExecutionException { + private void createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( + RdbEngine rdbEngine, String... expectedSqlStatements) throws SQLException { + RdbEngineStrategy strategy = RdbEngine.createRdbEngineStrategy(rdbEngine); + createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( + strategy, expectedSqlStatements); + } + + private void createTableInternal_IfNotExistsForX_createTableAndIndexesIfNotExists( + RdbEngineStrategy rdbEngineStrategy, String... expectedSqlStatements) throws SQLException { // Arrange String namespace = "my_ns"; String table = "foo_table"; @@ -1220,10 +689,10 @@ private void createTable_WithClusteringOrderForX_shouldExecuteCreateTableStateme mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); when(dataSource.getConnection()).thenReturn(connection); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + JdbcAdmin admin = createJdbcAdminFor(rdbEngineStrategy); // Act - admin.createTable(namespace, table, metadata, new HashMap<>()); + admin.createTableInternal(connection, namespace, table, metadata, true); // Assert for (int i = 0; i < expectedSqlStatements.length; i++) { @@ -1232,212 +701,306 @@ private void createTable_WithClusteringOrderForX_shouldExecuteCreateTableStateme } @Test - public void - createMetadataTableIfNotExists_WithInternalDbError_forMysql_shouldThrowInternalDbError() - throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine.MYSQL, new CommunicationsException("", null)); - } - - @Test - public void - createMetadataTableIfNotExists_WithInternalDbError_forPostgresql_shouldThrowInternalDbError() - throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine.POSTGRESQL, new PSQLException("", PSQLState.CONNECTION_FAILURE)); - } - - @Test - public void - createMetadataTableIfNotExists_WithInternalDbError_forSqlite_shouldThrowInternalDbError() - throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine.SQLITE, new SQLiteException("", SQLiteErrorCode.SQLITE_IOERR)); - } - - @Test - public void createMetadataTableIfNotExists_WithInternalDbError_forDb2_shouldThrowInternalDbError() - throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine.DB2, new SQLException("")); - } - - private void createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine rdbEngine, SQLException internalDbError) throws SQLException { - // Arrange - when(connection.createStatement()).thenThrow(internalDbError); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - - // Act - // Assert - assertThatThrownBy(() -> admin.createMetadataTableIfNotExists(connection)) - .isInstanceOf(internalDbError.getClass()); - } - - @Test - public void truncateTable_forMysql_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.MYSQL, "TRUNCATE TABLE `my_ns`.`foo_table`"); + public void addTableMetadata_OverwriteMetadataForMysql_ShouldWorkProperly() throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.MYSQL, + "DELETE FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } @Test - public void truncateTable_forPostgresql_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.POSTGRESQL, "TRUNCATE TABLE \"my_ns\".\"foo_table\""); + public void addTableMetadata_OverwriteMetadataForPostgresql_ShouldWorkProperly() + throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.POSTGRESQL, + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } @Test - public void truncateTable_forSqlServer_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.SQL_SERVER, "TRUNCATE TABLE [my_ns].[foo_table]"); + public void addTableMetadata_OverwriteMetadataForSqlServer_ShouldWorkProperly() throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.SQL_SERVER, + "DELETE FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,2)"); } @Test - public void truncateTable_forOracle_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.ORACLE, "TRUNCATE TABLE \"my_ns\".\"foo_table\""); + public void addTableMetadata_OverwriteMetadataForOracle_ShouldWorkProperly() throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.ORACLE, + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,2)"); } @Test - public void truncateTable_forSqlite_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.SQLITE, "DELETE FROM \"my_ns$foo_table\""); + public void addTableMetadata_OverwriteMetadataForSqlite_ShouldWorkProperly() throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.SQLITE, + "DELETE FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,FALSE,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,FALSE,2)"); } @Test - public void truncateTable_forDb2_shouldExecuteTruncateTableStatement() - throws SQLException, ExecutionException { - truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine.DB2, "TRUNCATE TABLE \"my_ns\".\"foo_table\" IMMEDIATE"); + public void addTableMetadata_OverwriteMetadataForDb2_ShouldWorkProperly() throws Exception { + addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine.DB2, + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } - private void truncateTable_forX_shouldExecuteTruncateTableStatement( - RdbEngine rdbEngine, String expectedTruncateTableStatement) - throws SQLException, ExecutionException { + private void addTableMetadata_overwriteMetadataForX_ShouldWorkProperly( + RdbEngine rdbEngine, String... expectedSqlStatements) throws Exception { // Arrange String namespace = "my_ns"; String table = "foo_table"; - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("c1") + .addColumn("c1", DataType.TEXT) + .addColumn("c2", DataType.BIGINT) + .build(); - Statement truncateTableStatement = mock(Statement.class); - when(connection.createStatement()).thenReturn(truncateTableStatement); + List mockedStatements = new ArrayList<>(); + for (int i = 0; i < expectedSqlStatements.length; i++) { + mockedStatements.add(mock(Statement.class)); + } + when(connection.createStatement()) + .thenReturn( + mockedStatements.get(0), + mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); when(dataSource.getConnection()).thenReturn(connection); - // Act - admin.truncateTable(namespace, table); + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + admin.addTableMetadata(connection, namespace, table, metadata, false, true); // Assert - verify(truncateTableStatement).execute(expectedTruncateTableStatement); + for (int i = 0; i < expectedSqlStatements.length; i++) { + verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); + } } @Test - public void dropTable_forMysqlWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForMysql_ShouldWorkProperly() throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.MYSQL, - "DROP TABLE `my_ns`.`foo_table`", + "CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`", + "CREATE TABLE IF NOT EXISTS `" + + METADATA_SCHEMA + + "`.`metadata`(" + + "`full_table_name` VARCHAR(128)," + + "`column_name` VARCHAR(128)," + + "`data_type` VARCHAR(20) NOT NULL," + + "`key_type` VARCHAR(20)," + + "`clustering_order` VARCHAR(10)," + + "`indexed` BOOLEAN NOT NULL," + + "`ordinal_position` INTEGER NOT NULL," + + "PRIMARY KEY (`full_table_name`, `column_name`))", "DELETE FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", - "SELECT DISTINCT `full_table_name` FROM `" + tableMetadataSchemaName + "`.`metadata`", - "DROP TABLE `" + tableMetadataSchemaName + "`.`metadata`", - "DROP SCHEMA `" + tableMetadataSchemaName + "`"); + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } @Test - public void - dropTable_forPostgresqlWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() - throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForPostgresql_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.POSTGRESQL, - "DROP TABLE \"my_ns\".\"foo_table\"", + "CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128)," + + "\"column_name\" VARCHAR(128)," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP TABLE \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP SCHEMA \"" + tableMetadataSchemaName + "\""); + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } @Test - public void - dropTable_forSqlServerWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() - throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForSqlServer_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.SQL_SERVER, - "DROP TABLE [my_ns].[foo_table]", + "CREATE SCHEMA [" + METADATA_SCHEMA + "]", + "CREATE TABLE [" + + METADATA_SCHEMA + + "].[metadata](" + + "[full_table_name] VARCHAR(128)," + + "[column_name] VARCHAR(128)," + + "[data_type] VARCHAR(20) NOT NULL," + + "[key_type] VARCHAR(20)," + + "[clustering_order] VARCHAR(10)," + + "[indexed] BIT NOT NULL," + + "[ordinal_position] INTEGER NOT NULL," + + "PRIMARY KEY ([full_table_name], [column_name]))", "DELETE FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", - "SELECT DISTINCT [full_table_name] FROM [" + tableMetadataSchemaName + "].[metadata]", - "DROP TABLE [" + tableMetadataSchemaName + "].[metadata]", - "DROP SCHEMA [" + tableMetadataSchemaName + "]"); + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,2)"); } @Test - public void dropTable_forOracleWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForOracle_ShouldWorkProperly() throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.ORACLE, - "DROP TABLE \"my_ns\".\"foo_table\"", + "CREATE USER \"" + METADATA_SCHEMA + "\" IDENTIFIED BY \"Oracle1234!@#$\"", + "ALTER USER \"" + METADATA_SCHEMA + "\" quota unlimited on USERS", + "CREATE TABLE \"" + + METADATA_SCHEMA + + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP TABLE \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP USER \"" + tableMetadataSchemaName + "\""); + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,2)"); } @Test - public void dropTable_forSqliteWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForSqlite_ShouldWorkProperly() throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.SQLITE, - "DROP TABLE \"my_ns$foo_table\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "$metadata\"(" + + "\"full_table_name\" TEXT," + + "\"column_name\" TEXT," + + "\"data_type\" TEXT NOT NULL," + + "\"key_type\" TEXT," + + "\"clustering_order\" TEXT," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "$metadata\"", - "DROP TABLE \"" + tableMetadataSchemaName + "$metadata\""); + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,FALSE,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,FALSE,2)"); } @Test - public void dropTable_forDb2WithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + public void addTableMetadata_ifNotExistsAndOverwriteMetadataForDb2_ShouldWorkProperly() throws Exception { - dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.DB2, - "DROP TABLE \"my_ns\".\"foo_table\"", + "CREATE SCHEMA \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128) NOT NULL," + + "\"column_name\" VARCHAR(128) NOT NULL," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP TABLE \"" + tableMetadataSchemaName + "\".\"metadata\"", - "DROP SCHEMA \"" + tableMetadataSchemaName + "\" RESTRICT"); + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,2)"); } - private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( - RdbEngine rdbEngine, String... expectedSqlStatements) throws Exception { + private void + addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( + RdbEngine rdbEngine, String... expectedSqlStatements) throws Exception { // Arrange String namespace = "my_ns"; String table = "foo_table"; - - ResultSet resultSet = mock(ResultSet.class); - when(resultSet.next()).thenReturn(false); + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("c1") + .addColumn("c1", DataType.TEXT) + .addColumn("c2", DataType.BIGINT) + .build(); List mockedStatements = new ArrayList<>(); - for (String expectedSqlStatement : expectedSqlStatements) { - Statement mock = mock(Statement.class); - mockedStatements.add(mock); - if (expectedSqlStatement.startsWith("SELECT ")) { - when(mock.executeQuery(any())).thenReturn(resultSet); - } + for (int i = 0; i < expectedSqlStatements.length; i++) { + mockedStatements.add(mock(Statement.class)); } - when(connection.createStatement()) .thenReturn( mockedStatements.get(0), @@ -1447,101 +1010,908 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel JdbcAdmin admin = createJdbcAdminFor(rdbEngine); // Act - admin.dropTable(namespace, table); + admin.addTableMetadata(connection, namespace, table, metadata, true, true); // Assert for (int i = 0; i < expectedSqlStatements.length; i++) { - if (expectedSqlStatements[i].startsWith("SELECT ")) { - verify(mockedStatements.get(i)).executeQuery(expectedSqlStatements[i]); - } else { - verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); - } + verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); } } @Test - public void - dropTable_forMysqlWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() - throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForMysql_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.MYSQL, - "DROP TABLE `my_ns`.`foo_table`", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", - "SELECT DISTINCT `full_table_name` FROM `" + tableMetadataSchemaName + "`.`metadata`"); + "CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`", + "CREATE TABLE IF NOT EXISTS `" + + METADATA_SCHEMA + + "`.`metadata`(" + + "`full_table_name` VARCHAR(128)," + + "`column_name` VARCHAR(128)," + + "`data_type` VARCHAR(20) NOT NULL," + + "`key_type` VARCHAR(20)," + + "`clustering_order` VARCHAR(10)," + + "`indexed` BOOLEAN NOT NULL," + + "`ordinal_position` INTEGER NOT NULL," + + "PRIMARY KEY (`full_table_name`, `column_name`))", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',true,3)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,false,5)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", + "INSERT INTO `scalardb`.`metadata` VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", + "INSERT INTO `scalardb`.`metadata` VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", + "INSERT INTO `scalardb`.`metadata` VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", + "INSERT INTO `scalardb`.`metadata` VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); } @Test public void - dropTable_forPostgresqlWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForPostgresql_ShouldWorkProperly() throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, - "DROP TABLE \"my_ns\".\"foo_table\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); + "CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128)," + + "\"column_name\" VARCHAR(128)," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',true,3)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,false,5)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); } @Test - public void - dropTable_forSqlServerWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() - throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForSqlServer_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, - "DROP TABLE [my_ns].[foo_table]", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", - "SELECT DISTINCT [full_table_name] FROM [" + tableMetadataSchemaName + "].[metadata]"); + "CREATE SCHEMA [" + METADATA_SCHEMA + "]", + "CREATE TABLE [" + + METADATA_SCHEMA + + "].[metadata](" + + "[full_table_name] VARCHAR(128)," + + "[column_name] VARCHAR(128)," + + "[data_type] VARCHAR(20) NOT NULL," + + "[key_type] VARCHAR(20)," + + "[clustering_order] VARCHAR(10)," + + "[indexed] BIT NOT NULL," + + "[ordinal_position] INTEGER NOT NULL," + + "PRIMARY KEY ([full_table_name], [column_name]))", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',1,2)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',1,3)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,0,5)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)", + "INSERT INTO [scalardb].[metadata] VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,0,8)", + "INSERT INTO [scalardb].[metadata] VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,0,9)", + "INSERT INTO [scalardb].[metadata] VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,0,10)", + "INSERT INTO [scalardb].[metadata] VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,0,11)"); } @Test - public void - dropTable_forOracleWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() - throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForOracle_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.ORACLE, - "DROP TABLE \"my_ns\".\"foo_table\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); + "CREATE USER \"" + METADATA_SCHEMA + "\" IDENTIFIED BY \"Oracle1234!@#$\"", + "ALTER USER \"" + METADATA_SCHEMA + "\" quota unlimited on USERS", + "CREATE TABLE \"" + + METADATA_SCHEMA + + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,0,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',1,2)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',1,3)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,0,4)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,0,5)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,0,6)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,0,7)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,0,8)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,0,9)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,0,10)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,0,11)"); } @Test - public void - dropTable_forSqliteWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() - throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForSqlite_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.SQLITE, - "DROP TABLE \"my_ns$foo_table\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "$metadata\""); - } - - @Test - public void - dropTable_forDb2WithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() - throws Exception { - dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( - RdbEngine.DB2, - "DROP TABLE \"my_ns\".\"foo_table\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); - } + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "$metadata\"(" + + "\"full_table_name\" TEXT," + + "\"column_name\" TEXT," + + "\"data_type\" TEXT NOT NULL," + + "\"key_type\" TEXT," + + "\"clustering_order\" TEXT," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,FALSE,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',TRUE,2)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',TRUE,3)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,FALSE,4)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,FALSE,5)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,FALSE,6)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,FALSE,7)", + "INSERT INTO \"scalardb$metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,FALSE,8)", + "INSERT INTO \"scalardb$metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,FALSE,9)", + "INSERT INTO \"scalardb$metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,FALSE,10)", + "INSERT INTO \"scalardb$metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,FALSE,11)"); + } + + @Test + public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForDb2_ShouldWorkProperly() + throws Exception { + addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( + RdbEngine.DB2, + "CREATE SCHEMA \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128) NOT NULL," + + "\"column_name\" VARCHAR(128) NOT NULL," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c3','BOOLEAN','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','CLUSTERING','DESC',true,2)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c4','BLOB','CLUSTERING','ASC',true,3)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c2','BIGINT',NULL,NULL,false,4)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c5','INT',NULL,NULL,false,5)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c6','DOUBLE',NULL,NULL,false,6)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c7','FLOAT',NULL,NULL,false,7)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c8','DATE',NULL,NULL,false,8)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c9','TIME',NULL,NULL,false,9)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c10','TIMESTAMP',NULL,NULL,false,10)", + "INSERT INTO \"scalardb\".\"metadata\" VALUES ('my_ns.foo_table','c11','TIMESTAMPTZ',NULL,NULL,false,11)"); + } + + private void addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( + RdbEngine rdbEngine, String... expectedSqlStatements) throws Exception { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("c3") + .addClusteringKey("c1", Order.DESC) + .addClusteringKey("c4", Order.ASC) + .addColumn("c1", DataType.TEXT) + .addColumn("c2", DataType.BIGINT) + .addColumn("c3", DataType.BOOLEAN) + .addColumn("c4", DataType.BLOB) + .addColumn("c5", DataType.INT) + .addColumn("c6", DataType.DOUBLE) + .addColumn("c7", DataType.FLOAT) + .addColumn("c8", DataType.DATE) + .addColumn("c9", DataType.TIME) + .addColumn("c10", DataType.TIMESTAMP) + .addColumn("c11", DataType.TIMESTAMPTZ) + .addSecondaryIndex("c1") + .addSecondaryIndex("c4") + .build(); + + List mockedStatements = new ArrayList<>(); + for (int i = 0; i < expectedSqlStatements.length; i++) { + mockedStatements.add(mock(Statement.class)); + } + when(connection.createStatement()) + .thenReturn( + mockedStatements.get(0), + mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); + when(dataSource.getConnection()).thenReturn(connection); + + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + admin.addTableMetadata(connection, namespace, table, metadata, true, false); + + // Assert + for (int i = 0; i < expectedSqlStatements.length; i++) { + verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); + } + } + + @ParameterizedTest + @EnumSource(RdbEngine.class) + public void createTable_ShouldCallCreateTableAndAddTableMetadataCorrectly(RdbEngine rdbEngine) + throws SQLException, ExecutionException { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("c3") + .addClusteringKey("c1", Order.DESC) + .addClusteringKey("c5", Order.ASC) + .addColumn("c1", DataType.TEXT) + .addColumn("c2", DataType.BIGINT) + .addColumn("c3", DataType.BOOLEAN) + .addColumn("c4", DataType.BLOB) + .addColumn("c5", DataType.INT) + .addColumn("c6", DataType.DOUBLE) + .addColumn("c7", DataType.FLOAT) + .addColumn("c8", DataType.DATE) + .addColumn("c9", DataType.TIME) + .addColumn("c10", DataType.TIMESTAMP) + .addColumn("c11", DataType.TIMESTAMPTZ) + .addSecondaryIndex("c1") + .addSecondaryIndex("c5") + .build(); + when(connection.createStatement()).thenReturn(mock(Statement.class)); + when(dataSource.getConnection()).thenReturn(connection); + + JdbcAdmin adminSpy = spy(createJdbcAdminFor(rdbEngine)); + + // Act + adminSpy.createTable(namespace, table, metadata, Collections.emptyMap()); + + // Assert + verify(adminSpy).createTableInternal(connection, namespace, table, metadata, false); + verify(adminSpy).addTableMetadata(connection, namespace, table, metadata, true, false); + } + + @Test + public void repairTable_ForMysql_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.MYSQL, + "SELECT 1 FROM `my_ns`.`foo_table` LIMIT 1", + "CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`", + "CREATE TABLE IF NOT EXISTS `" + + METADATA_SCHEMA + + "`.`metadata`(" + + "`full_table_name` VARCHAR(128)," + + "`column_name` VARCHAR(128)," + + "`data_type` VARCHAR(20) NOT NULL," + + "`key_type` VARCHAR(20)," + + "`clustering_order` VARCHAR(10)," + + "`indexed` BOOLEAN NOT NULL," + + "`ordinal_position` INTEGER NOT NULL," + + "PRIMARY KEY (`full_table_name`, `column_name`))", + "DELETE FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", + "INSERT INTO `" + + METADATA_SCHEMA + + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + } + + @Test + public void repairTable_ForOracle_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.ORACLE, + "SELECT 1 FROM \"my_ns\".\"foo_table\" FETCH FIRST 1 ROWS ONLY", + "CREATE USER \"" + METADATA_SCHEMA + "\" IDENTIFIED BY \"Oracle1234!@#$\"", + "ALTER USER \"" + METADATA_SCHEMA + "\" quota unlimited on USERS", + "CREATE TABLE \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR2(128)," + + "\"column_name\" VARCHAR2(128)," + + "\"data_type\" VARCHAR2(20) NOT NULL," + + "\"key_type\" VARCHAR2(20)," + + "\"clustering_order\" VARCHAR2(10)," + + "\"indexed\" NUMBER(1) NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); + } + + @Test + public void repairTable_ForPosgresql_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.POSTGRESQL, + "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", + "CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128)," + + "\"column_name\" VARCHAR(128)," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + } + + @Test + public void repairTable_ForSqlServer_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.SQL_SERVER, + "SELECT TOP 1 1 FROM [my_ns].[foo_table]", + "CREATE SCHEMA [" + METADATA_SCHEMA + "]", + "CREATE TABLE [" + + METADATA_SCHEMA + + "].[metadata](" + + "[full_table_name] VARCHAR(128)," + + "[column_name] VARCHAR(128)," + + "[data_type] VARCHAR(20) NOT NULL," + + "[key_type] VARCHAR(20)," + + "[clustering_order] VARCHAR(10)," + + "[indexed] BIT NOT NULL," + + "[ordinal_position] INTEGER NOT NULL," + + "PRIMARY KEY ([full_table_name], [column_name]))", + "DELETE FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); + } + + @Test + public void repairTable_ForSqlite_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.SQLITE, + "SELECT 1 FROM \"my_ns$foo_table\" LIMIT 1", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "$metadata\"(" + + "\"full_table_name\" TEXT," + + "\"column_name\" TEXT," + + "\"data_type\" TEXT NOT NULL," + + "\"key_type\" TEXT," + + "\"clustering_order\" TEXT," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "DELETE FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,FALSE,1)"); + } + + @Test + public void repairTable_ForDb2_shouldCreateMetadataTableAndAddMetadataForTable() + throws SQLException, ExecutionException { + repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine.DB2, + "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", + "CREATE SCHEMA \"" + METADATA_SCHEMA + "\"", + "CREATE TABLE IF NOT EXISTS \"" + + METADATA_SCHEMA + + "\".\"metadata\"(" + + "\"full_table_name\" VARCHAR(128) NOT NULL," + + "\"column_name\" VARCHAR(128) NOT NULL," + + "\"data_type\" VARCHAR(20) NOT NULL," + + "\"key_type\" VARCHAR(20)," + + "\"clustering_order\" VARCHAR(10)," + + "\"indexed\" BOOLEAN NOT NULL," + + "\"ordinal_position\" INTEGER NOT NULL," + + "PRIMARY KEY (\"full_table_name\", \"column_name\"))", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + } + + private void repairTable_ForX_shouldCreateMetadataTableAndAddMetadataForTable( + RdbEngine rdbEngine, String... expectedSqlStatements) + throws SQLException, ExecutionException { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + TableMetadata metadata = + TableMetadata.newBuilder().addPartitionKey("c1").addColumn("c1", DataType.TEXT).build(); + + List mockedStatements = new ArrayList<>(); + for (int i = 0; i < expectedSqlStatements.length; i++) { + mockedStatements.add(mock(Statement.class)); + } + + when(connection.createStatement()) + .thenReturn( + mockedStatements.get(0), + mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); + when(dataSource.getConnection()).thenReturn(connection); + + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + admin.repairTable(namespace, table, metadata, new HashMap<>()); + + // Assert + for (int i = 0; i < expectedSqlStatements.length; i++) { + verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); + } + } + + @Test + public void repairTable_WithNonExistingTableToRepairForMysql_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.MYSQL, "SELECT 1 FROM `my_ns`.`foo_table` LIMIT 1"); + } + + @Test + public void + repairTable_WithNonExistingTableToRepairForOracle_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.ORACLE, "SELECT 1 FROM \"my_ns\".\"foo_table\" FETCH FIRST 1 ROWS ONLY"); + } + + @Test + public void + repairTable_WithNonExistingTableToRepairForPostgresql_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.POSTGRESQL, "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1"); + } + + @Test + public void + repairTable_WithNonExistingTableToRepairForSqlServer_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.SQL_SERVER, "SELECT TOP 1 1 FROM [my_ns].[foo_table]"); + } + + @Test + public void + repairTable_WithNonExistingTableToRepairForSqlite_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.SQLITE, "SELECT 1 FROM \"my_ns$foo_table\" LIMIT 1"); + } + + @Test + public void repairTable_WithNonExistingTableToRepairForDb2_shouldThrowIllegalArgumentException() + throws SQLException { + repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine.DB2, "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1"); + } + + private void repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( + RdbEngine rdbEngine, String expectedCheckTableExistStatement) throws SQLException { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + TableMetadata metadata = + TableMetadata.newBuilder().addPartitionKey("c1").addColumn("c1", DataType.TEXT).build(); + + Statement checkTableExistStatement = mock(Statement.class); + when(connection.createStatement()).thenReturn(checkTableExistStatement); + when(dataSource.getConnection()).thenReturn(connection); + + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + SQLException sqlException = mock(SQLException.class); + mockUndefinedTableError(rdbEngine, sqlException); + when(checkTableExistStatement.execute(any())).thenThrow(sqlException); + + // Act + assertThatThrownBy(() -> admin.repairTable(namespace, table, metadata, new HashMap<>())) + .isInstanceOf(IllegalArgumentException.class); + + // Assert + verify(checkTableExistStatement).execute(expectedCheckTableExistStatement); + } + + @Test + public void + createMetadataTableIfNotExists_WithInternalDbError_forMysql_shouldThrowInternalDbError() + throws SQLException { + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + RdbEngine.MYSQL, new CommunicationsException("", null)); + } + + @Test + public void + createMetadataTableIfNotExists_WithInternalDbError_forPostgresql_shouldThrowInternalDbError() + throws SQLException { + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + RdbEngine.POSTGRESQL, new PSQLException("", PSQLState.CONNECTION_FAILURE)); + } + + @Test + public void + createMetadataTableIfNotExists_WithInternalDbError_forSqlite_shouldThrowInternalDbError() + throws SQLException { + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + RdbEngine.SQLITE, new SQLiteException("", SQLiteErrorCode.SQLITE_IOERR)); + } + + private void + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + RdbEngine rdbEngine, SQLException internalDbError) throws SQLException { + // Arrange + when(connection.createStatement()).thenThrow(internalDbError); + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + // Assert + assertThatThrownBy(() -> admin.createTableMetadataTableIfNotExists(connection)) + .isInstanceOf(internalDbError.getClass()); + } + + @Test + public void truncateTable_forMysql_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.MYSQL, "TRUNCATE TABLE `my_ns`.`foo_table`"); + } + + @Test + public void truncateTable_forPostgresql_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.POSTGRESQL, "TRUNCATE TABLE \"my_ns\".\"foo_table\""); + } + + @Test + public void truncateTable_forSqlServer_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.SQL_SERVER, "TRUNCATE TABLE [my_ns].[foo_table]"); + } + + @Test + public void truncateTable_forOracle_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.ORACLE, "TRUNCATE TABLE \"my_ns\".\"foo_table\""); + } + + @Test + public void truncateTable_forSqlite_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.SQLITE, "DELETE FROM \"my_ns$foo_table\""); + } + + @Test + public void truncateTable_forDb2_shouldExecuteTruncateTableStatement() + throws SQLException, ExecutionException { + truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine.DB2, "TRUNCATE TABLE \"my_ns\".\"foo_table\" IMMEDIATE"); + } + + private void truncateTable_forX_shouldExecuteTruncateTableStatement( + RdbEngine rdbEngine, String expectedTruncateTableStatement) + throws SQLException, ExecutionException { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + Statement truncateTableStatement = mock(Statement.class); + when(connection.createStatement()).thenReturn(truncateTableStatement); + when(dataSource.getConnection()).thenReturn(connection); + + // Act + admin.truncateTable(namespace, table); + + // Assert + verify(truncateTableStatement).execute(expectedTruncateTableStatement); + } + + @Test + public void dropTable_forMysqlWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.MYSQL, + "DROP TABLE `my_ns`.`foo_table`", + "DELETE FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`", + "DROP TABLE `" + METADATA_SCHEMA + "`.`metadata`"); + } + + @Test + public void + dropTable_forPostgresqlWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.POSTGRESQL, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void + dropTable_forSqlServerWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.SQL_SERVER, + "DROP TABLE [my_ns].[foo_table]", + "DELETE FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]", + "DROP TABLE [" + METADATA_SCHEMA + "].[metadata]"); + } + + @Test + public void dropTable_forOracleWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.ORACLE, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void dropTable_forSqliteWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.SQLITE, + "DROP TABLE \"my_ns$foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\"", + "DROP TABLE \"" + METADATA_SCHEMA + "$metadata\""); + } + + @Test + public void dropTable_forDb2WithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata() + throws Exception { + dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine.DB2, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( + RdbEngine rdbEngine, String... expectedSqlStatements) throws Exception { + // Arrange + String namespace = "my_ns"; + String table = "foo_table"; + + ResultSet resultSet = mock(ResultSet.class); + when(resultSet.next()).thenReturn(false); + + List mockedStatements = new ArrayList<>(); + for (String expectedSqlStatement : expectedSqlStatements) { + Statement mock = mock(Statement.class); + mockedStatements.add(mock); + if (expectedSqlStatement.startsWith("SELECT ")) { + when(mock.executeQuery(any())).thenReturn(resultSet); + } + } + + when(connection.createStatement()) + .thenReturn( + mockedStatements.get(0), + mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); + when(dataSource.getConnection()).thenReturn(connection); + + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + admin.dropTable(namespace, table); + + // Assert + for (int i = 0; i < expectedSqlStatements.length; i++) { + if (expectedSqlStatements[i].startsWith("SELECT ")) { + verify(mockedStatements.get(i)).executeQuery(expectedSqlStatements[i]); + } else { + verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); + } + } + } + + @Test + public void + dropTable_forMysqlWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.MYSQL, + "DROP TABLE `my_ns`.`foo_table`", + "DELETE FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`"); + } + + @Test + public void + dropTable_forPostgresqlWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.POSTGRESQL, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void + dropTable_forSqlServerWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.SQL_SERVER, + "DROP TABLE [my_ns].[foo_table]", + "DELETE FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]"); + } + + @Test + public void + dropTable_forOracleWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.ORACLE, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void + dropTable_forSqliteWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.SQLITE, + "DROP TABLE \"my_ns$foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\""); + } + + @Test + public void + dropTable_forDb2WithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable() + throws Exception { + dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( + RdbEngine.DB2, + "DROP TABLE \"my_ns\".\"foo_table\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } private void dropTable_forXWithOtherMetadataAfterDeletion_ShouldDropTableAndDeleteMetadataButNotMetadataTable( @@ -1710,7 +2080,7 @@ public void getNamespaceTables_forMysql_ShouldReturnTableNames() throws Exceptio getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.MYSQL, "SELECT DISTINCT `full_table_name` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` LIKE ?"); } @@ -1719,7 +2089,7 @@ public void getNamespaceTables_forPostgresql_ShouldReturnTableNames() throws Exc getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.POSTGRESQL, "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" LIKE ?"); } @@ -1728,7 +2098,7 @@ public void getNamespaceTables_forSqlServer_ShouldReturnTableNames() throws Exce getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.SQL_SERVER, "SELECT DISTINCT [full_table_name] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] LIKE ?"); } @@ -1737,7 +2107,7 @@ public void getNamespaceTables_forOracle_ShouldReturnTableNames() throws Excepti getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.ORACLE, "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" LIKE ?"); } @@ -1746,7 +2116,7 @@ public void getNamespaceTables_forSqlite_ShouldReturnTableNames() throws Excepti getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.SQLITE, "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" LIKE ?"); } @@ -1755,7 +2125,7 @@ public void getNamespaceTables_forDb2_ShouldReturnTableNames() throws Exception getNamespaceTables_forX_ShouldReturnTableNames( RdbEngine.DB2, "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" LIKE ?"); } @@ -1770,10 +2140,11 @@ private void getNamespaceTables_forX_ShouldReturnTableNames( // Everytime the ResultSet.next() method will be called, the ResultSet.getXXX methods call be // mocked to return the current row data doAnswer( - new GetTablesNamesResultSetMocker( + new SelectFullTableNameFromMetadataTableResultSetMocker( Arrays.asList( - new GetTablesNamesResultSetMocker.Row(namespace + ".t1"), - new GetTablesNamesResultSetMocker.Row(namespace + ".t2")))) + new SelectFullTableNameFromMetadataTableResultSetMocker.Row(namespace + ".t1"), + new SelectFullTableNameFromMetadataTableResultSetMocker.Row( + namespace + ".t2")))) .when(resultSet) .next(); PreparedStatement preparedStatement = mock(PreparedStatement.class); @@ -1868,375 +2239,122 @@ private void namespaceExists_forXWithExistingNamespace_ShouldReturnTrue( } verify(selectStatement).executeQuery(); verify(connection).prepareStatement(expectedSelectStatement); - verify(selectStatement).setString(1, namespace + namespacePlaceholderSuffix); - } - - @Test - public void namespaceExists_WithMetadataSchema_ShouldReturnTrue() throws ExecutionException { - // Arrange - JdbcAdmin adminForMySql = createJdbcAdminFor(RdbEngine.MYSQL); - JdbcAdmin adminForPostgresql = createJdbcAdminFor(RdbEngine.POSTGRESQL); - JdbcAdmin adminForOracle = createJdbcAdminFor(RdbEngine.ORACLE); - JdbcAdmin adminForSqlServer = createJdbcAdminFor(RdbEngine.SQL_SERVER); - JdbcAdmin adminForSqlite = createJdbcAdminFor(RdbEngine.SQLITE); - JdbcAdmin adminForYugabyte = createJdbcAdminFor(RdbEngine.YUGABYTE); - JdbcAdmin adminForDb2 = createJdbcAdminFor(RdbEngine.DB2); - - // Act Assert - assertThat(adminForMySql.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForPostgresql.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForOracle.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForSqlServer.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForSqlite.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForYugabyte.namespaceExists(tableMetadataSchemaName)).isTrue(); - assertThat(adminForDb2.namespaceExists(tableMetadataSchemaName)).isTrue(); - } - - @Test - public void createIndex_ForColumnTypeWithoutRequiredAlterationForMysql_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.MYSQL, - "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", - "CREATE INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl` (`my_column`)", - "UPDATE `" - + tableMetadataSchemaName - + "`.`metadata` SET `indexed`=true WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); - } - - @Test - public void - createIndex_ForColumnTypeWithoutRequiredAlterationForPostgresql_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.POSTGRESQL, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - @Test - public void - createIndex_ForColumnTypeWithoutRequiredAlterationForSqlServer_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.SQL_SERVER, - "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", - "CREATE INDEX [index_my_ns_my_tbl_my_column] ON [my_ns].[my_tbl] ([my_column])", - "UPDATE [" - + tableMetadataSchemaName - + "].[metadata] SET [indexed]=1 WHERE [full_table_name]='my_ns.my_tbl' AND [column_name]='my_column'"); - } - - @Test - public void - createIndex_ForColumnTypeWithoutRequiredAlterationForOracle_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.ORACLE, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=1 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - @Test - public void - createIndex_ForColumnTypeWithoutRequiredAlterationForSqlite_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.SQLITE, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns$my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "$metadata\" SET \"indexed\"=TRUE WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - @Test - public void createIndex_ForColumnTypeWithoutRequiredAlterationForDb2_ShouldCreateIndexProperly() - throws Exception { - createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine.DB2, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - private void createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( - RdbEngine rdbEngine, - String expectedGetTableMetadataStatement, - String expectedCreateIndexStatement, - String expectedUpdateTableMetadataStatement) - throws SQLException, ExecutionException { - // Arrange - String namespace = "my_ns"; - String table = "my_tbl"; - String indexColumn = "my_column"; - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); - PreparedStatement selectStatement = mock(PreparedStatement.class); - ResultSet resultSet = - mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.BOOLEAN.toString(), null, null, false))); - when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); - Statement statement = mock(Statement.class); - - when(dataSource.getConnection()).thenReturn(connection); - when(connection.createStatement()).thenReturn(statement); - - // Act - admin.createIndex(namespace, table, indexColumn, Collections.emptyMap()); - - // Assert - verify(connection).prepareStatement(expectedGetTableMetadataStatement); - verify(selectStatement).setString(1, getFullTableName(namespace, table)); - - verify(connection, times(2)).createStatement(); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(statement, times(2)).execute(captor.capture()); - assertThat(captor.getAllValues().get(0)).isEqualTo(expectedCreateIndexStatement); - assertThat(captor.getAllValues().get(1)).isEqualTo(expectedUpdateTableMetadataStatement); - } - - @Test - public void - createIndex_forColumnTypeWithRequiredAlterationForMysql_ShouldAlterColumnAndCreateIndexProperly() - throws Exception { - createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( - RdbEngine.MYSQL, - "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", - new String[] {"ALTER TABLE `my_ns`.`my_tbl` MODIFY `my_column` VARCHAR(128)"}, - "CREATE INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl` (`my_column`)", - "UPDATE `" - + tableMetadataSchemaName - + "`.`metadata` SET `indexed`=true WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); - } - - @Test - public void - createIndex_forColumnTypeWithRequiredAlterationForPostgresql_ShouldAlterColumnAndCreateIndexProperly() - throws Exception { - createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( - RdbEngine.POSTGRESQL, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - new String[] { - "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" TYPE VARCHAR(10485760)" - }, - "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - @Test - public void - createIndex_forColumnTypeWithRequiredAlterationForOracle_ShouldAlterColumnAndCreateIndexProperly() - throws Exception { - createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( - RdbEngine.ORACLE, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" MODIFY ( \"my_column\" VARCHAR2(128) )"}, - "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=1 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - @Test - public void - createIndex_forColumnTypeWithRequiredAlterationForSqlite_ShouldAlterColumnAndCreateIndexProperly() { - // SQLite does not require column type change on CREATE INDEX. - } - - @Test - public void - createIndex_forColumnTypeWithRequiredAlterationForDb2_ShouldAlterColumnAndCreateIndexProperly() - throws Exception { - createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( - RdbEngine.DB2, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - new String[] { - "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" SET DATA TYPE VARCHAR(128)", - "CALL SYSPROC.ADMIN_CMD('REORG TABLE \"my_ns\".\"my_tbl\"')" - }, - "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", - "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - private void - createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( - RdbEngine rdbEngine, - String expectedGetTableMetadataStatement, - String[] expectedAlterColumnTypeStatements, - String expectedCreateIndexStatement, - String expectedUpdateTableMetadataStatement) - throws SQLException, ExecutionException { - // Arrange - String namespace = "my_ns"; - String table = "my_tbl"; - String indexColumn = "my_column"; - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); - PreparedStatement selectStatement = mock(PreparedStatement.class); - ResultSet resultSet = - mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.TEXT.toString(), null, null, false))); - when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); - - Statement statement = mock(Statement.class); - - when(dataSource.getConnection()).thenReturn(connection); - when(connection.createStatement()).thenReturn(statement); - - // Act - admin.createIndex(namespace, table, indexColumn, Collections.emptyMap()); + verify(selectStatement).setString(1, namespace + namespacePlaceholderSuffix); + } - // Assert - verify(connection).prepareStatement(expectedGetTableMetadataStatement); - verify(selectStatement).setString(1, getFullTableName(namespace, table)); + @Test + public void namespaceExists_WithMetadataSchema_ShouldReturnTrue() throws ExecutionException { + // Arrange + JdbcAdmin adminForMySql = createJdbcAdminFor(RdbEngine.MYSQL); + JdbcAdmin adminForPostgresql = createJdbcAdminFor(RdbEngine.POSTGRESQL); + JdbcAdmin adminForOracle = createJdbcAdminFor(RdbEngine.ORACLE); + JdbcAdmin adminForSqlServer = createJdbcAdminFor(RdbEngine.SQL_SERVER); + JdbcAdmin adminForSqlite = createJdbcAdminFor(RdbEngine.SQLITE); + JdbcAdmin adminForYugabyte = createJdbcAdminFor(RdbEngine.YUGABYTE); + JdbcAdmin adminForDb2 = createJdbcAdminFor(RdbEngine.DB2); - verify(connection, times(2 + expectedAlterColumnTypeStatements.length)).createStatement(); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(statement, times(2 + expectedAlterColumnTypeStatements.length)) - .execute(captor.capture()); - for (int i = 0; i < expectedAlterColumnTypeStatements.length; i++) { - assertThat(captor.getAllValues().get(i)).isEqualTo(expectedAlterColumnTypeStatements[i]); - } - assertThat(captor.getAllValues().get(expectedAlterColumnTypeStatements.length)) - .isEqualTo(expectedCreateIndexStatement); - assertThat(captor.getAllValues().get(expectedAlterColumnTypeStatements.length + 1)) - .isEqualTo(expectedUpdateTableMetadataStatement); + // Act Assert + assertThat(adminForMySql.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForPostgresql.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForOracle.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForSqlServer.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForSqlite.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForYugabyte.namespaceExists(METADATA_SCHEMA)).isTrue(); + assertThat(adminForDb2.namespaceExists(METADATA_SCHEMA)).isTrue(); } @Test - public void dropIndex_forColumnTypeWithoutRequiredAlterationForMysql_ShouldDropIndexProperly() + public void createIndex_ForColumnTypeWithoutRequiredAlterationForMysql_ShouldCreateIndexProperly() throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", - "DROP INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl`", + "CREATE INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl` (`my_column`)", "UPDATE `" - + tableMetadataSchemaName - + "`.`metadata` SET `indexed`=false WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); + + METADATA_SCHEMA + + "`.`metadata` SET `indexed`=true WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); } @Test public void - dropIndex_forColumnTypeWithoutRequiredAlterationForPostgresql_ShouldDropIndexProperly() + createIndex_ForColumnTypeWithoutRequiredAlterationForPostgresql_ShouldCreateIndexProperly() throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void dropIndex_forColumnTypeWithoutRequiredAlterationForServer_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_ForColumnTypeWithoutRequiredAlterationForSqlServer_ShouldCreateIndexProperly() + throws Exception { + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", - "DROP INDEX [index_my_ns_my_tbl_my_column] ON [my_ns].[my_tbl]", + "CREATE INDEX [index_my_ns_my_tbl_my_column] ON [my_ns].[my_tbl] ([my_column])", "UPDATE [" - + tableMetadataSchemaName - + "].[metadata] SET [indexed]=0 WHERE [full_table_name]='my_ns.my_tbl' AND [column_name]='my_column'"); + + METADATA_SCHEMA + + "].[metadata] SET [indexed]=1 WHERE [full_table_name]='my_ns.my_tbl' AND [column_name]='my_column'"); } @Test - public void dropIndex_forColumnTypeWithoutRequiredAlterationForOracle_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_ForColumnTypeWithoutRequiredAlterationForOracle_ShouldCreateIndexProperly() + throws Exception { + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=0 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=1 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void dropIndex_forColumnTypeWithoutRequiredAlterationForSqlite_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_ForColumnTypeWithoutRequiredAlterationForSqlite_ShouldCreateIndexProperly() + throws Exception { + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.SQLITE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"index_my_ns_my_tbl_my_column\"", + "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns$my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "$metadata\" SET \"indexed\"=FALSE WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "$metadata\" SET \"indexed\"=TRUE WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void dropIndex_forColumnTypeWithoutRequiredAlterationForDb2_ShouldDropIndexProperly() + public void createIndex_ForColumnTypeWithoutRequiredAlterationForDb2_ShouldCreateIndexProperly() throws Exception { - dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } - private void dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + private void createIndex_ForColumnTypeWithoutRequiredAlterationForX_ShouldCreateIndexProperly( RdbEngine rdbEngine, String expectedGetTableMetadataStatement, - String expectedDropIndexStatement, + String expectedCreateIndexStatement, String expectedUpdateTableMetadataStatement) throws SQLException, ExecutionException { // Arrange @@ -2244,25 +2362,23 @@ private void dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropInde String table = "my_tbl"; String indexColumn = "my_column"; JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); + PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.BOOLEAN.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.BOOLEAN.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); - + when(connection.prepareStatement(any())).thenReturn(selectStatement); Statement statement = mock(Statement.class); when(dataSource.getConnection()).thenReturn(connection); when(connection.createStatement()).thenReturn(statement); // Act - admin.dropIndex(namespace, table, indexColumn); + admin.createIndex(namespace, table, indexColumn, Collections.emptyMap()); // Assert verify(connection).prepareStatement(expectedGetTableMetadataStatement); @@ -2271,454 +2387,370 @@ private void dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropInde verify(connection, times(2)).createStatement(); ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); verify(statement, times(2)).execute(captor.capture()); - assertThat(captor.getAllValues().get(0)).isEqualTo(expectedDropIndexStatement); + assertThat(captor.getAllValues().get(0)).isEqualTo(expectedCreateIndexStatement); assertThat(captor.getAllValues().get(1)).isEqualTo(expectedUpdateTableMetadataStatement); } @Test - public void dropIndex_forColumnTypeWithRequiredAlterationForMysql_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_forColumnTypeWithRequiredAlterationForMysql_ShouldAlterColumnAndCreateIndexProperly() + throws Exception { + createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", - "DROP INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl`", - new String[] {"ALTER TABLE `my_ns`.`my_tbl` MODIFY `my_column` LONGTEXT"}, + new String[] {"ALTER TABLE `my_ns`.`my_tbl` MODIFY `my_column` VARCHAR(128)"}, + "CREATE INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl` (`my_column`)", "UPDATE `" - + tableMetadataSchemaName - + "`.`metadata` SET `indexed`=false WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); + + METADATA_SCHEMA + + "`.`metadata` SET `indexed`=true WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); } @Test - public void dropIndex_forColumnTypeWithRequiredAlterationForPostgresql_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_forColumnTypeWithRequiredAlterationForPostgresql_ShouldAlterColumnAndCreateIndexProperly() + throws Exception { + createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", - new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" TYPE TEXT"}, + new String[] { + "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" TYPE VARCHAR(10485760)" + }, + "CREATE INDEX \"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void dropIndex_forColumnTypeWithRequiredAlterationForOracle_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_forColumnTypeWithRequiredAlterationForOracle_ShouldAlterColumnAndCreateIndexProperly() + throws Exception { + createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", - new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" MODIFY ( \"my_column\" VARCHAR2(4000) )"}, + new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" MODIFY ( \"my_column\" VARCHAR2(128) )"}, + "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=0 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=1 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void dropIndex_forColumnTypeWithRequiredAlterationForSqlite_ShouldDropIndexProperly() { + public void + createIndex_forColumnTypeWithRequiredAlterationForSqlite_ShouldAlterColumnAndCreateIndexProperly() { // SQLite does not require column type change on CREATE INDEX. } @Test - public void dropIndex_forColumnTypeWithRequiredAlterationForDb2_ShouldDropIndexProperly() - throws Exception { - dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + public void + createIndex_forColumnTypeWithRequiredAlterationForDb2_ShouldAlterColumnAndCreateIndexProperly() + throws Exception { + createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", new String[] { - "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" SET DATA TYPE VARCHAR(32672)", + "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" SET DATA TYPE VARCHAR(128)", "CALL SYSPROC.ADMIN_CMD('REORG TABLE \"my_ns\".\"my_tbl\"')" }, + "CREATE INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\" ON \"my_ns\".\"my_tbl\" (\"my_column\")", "UPDATE \"" - + tableMetadataSchemaName - + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); - } - - private void dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( - RdbEngine rdbEngine, - String expectedGetTableMetadataStatement, - String expectedDropIndexStatement, - String[] expectedAlterColumnStatements, - String expectedUpdateTableMetadataStatement) - throws SQLException, ExecutionException { - // Arrange - String namespace = "my_ns"; - String table = "my_tbl"; - String indexColumn = "my_column"; - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); - PreparedStatement selectStatement = mock(PreparedStatement.class); - ResultSet resultSet = - mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.TEXT.toString(), null, null, false))); - when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); - - Statement statement = mock(Statement.class); - - when(dataSource.getConnection()).thenReturn(connection); - when(connection.createStatement()).thenReturn(statement); - - // Act - admin.dropIndex(namespace, table, indexColumn); - - // Assert - verify(connection).prepareStatement(expectedGetTableMetadataStatement); - verify(selectStatement).setString(1, getFullTableName(namespace, table)); - - verify(connection, times(2 + expectedAlterColumnStatements.length)).createStatement(); - ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); - verify(statement, times(2 + expectedAlterColumnStatements.length)).execute(captor.capture()); - assertThat(captor.getAllValues().get(0)).isEqualTo(expectedDropIndexStatement); - for (int i = 0; i < expectedAlterColumnStatements.length; i++) { - assertThat(captor.getAllValues().get(i + 1)).isEqualTo(expectedAlterColumnStatements[i]); - } - assertThat(captor.getAllValues().get(expectedAlterColumnStatements.length + 1)) - .isEqualTo(expectedUpdateTableMetadataStatement); - } - - @Test - public void - repairTable_WithMissingMetadataTableForMysql_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.MYSQL, - "SELECT 1 FROM `my_ns`.`foo_table` LIMIT 1", - "SELECT 1 FROM `" + tableMetadataSchemaName + "`.`metadata` LIMIT 1", - "CREATE SCHEMA IF NOT EXISTS `" + tableMetadataSchemaName + "`", - "CREATE TABLE IF NOT EXISTS `" - + tableMetadataSchemaName - + "`.`metadata`(`full_table_name` VARCHAR(128),`column_name` VARCHAR(128),`data_type` VARCHAR(20) NOT NULL,`key_type` VARCHAR(20),`clustering_order` VARCHAR(10),`indexed` BOOLEAN NOT NULL,`ordinal_position` INTEGER NOT NULL,PRIMARY KEY (`full_table_name`, `column_name`))", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); - } - - @Test - public void - repairTable_WithMissingMetadataTableForOracle_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.ORACLE, - "SELECT 1 FROM \"my_ns\".\"foo_table\" FETCH FIRST 1 ROWS ONLY", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" FETCH FIRST 1 ROWS ONLY", - "CREATE USER \"" + tableMetadataSchemaName + "\" IDENTIFIED BY \"Oracle1234!@#$\"", - "ALTER USER \"" + tableMetadataSchemaName + "\" quota unlimited on USERS", - "CREATE TABLE \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR2(128),\"column_name\" VARCHAR2(128),\"data_type\" VARCHAR2(20) NOT NULL,\"key_type\" VARCHAR2(20),\"clustering_order\" VARCHAR2(10),\"indexed\" NUMBER(1) NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); - } - - @Test - public void - repairTable_WithMissingMetadataTableForPostgresql_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.POSTGRESQL, - "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" LIMIT 1", - "CREATE SCHEMA IF NOT EXISTS \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR(128),\"column_name\" VARCHAR(128),\"data_type\" VARCHAR(20) NOT NULL,\"key_type\" VARCHAR(20),\"clustering_order\" VARCHAR(10),\"indexed\" BOOLEAN NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); - } - - @Test - public void - repairTable_WithMissingMetadataTableForSqlServer_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.SQL_SERVER, - "SELECT TOP 1 1 FROM [my_ns].[foo_table]", - "SELECT TOP 1 1 FROM [" + tableMetadataSchemaName + "].[metadata]", - "CREATE SCHEMA [" + tableMetadataSchemaName + "]", - "CREATE TABLE [" - + tableMetadataSchemaName - + "].[metadata]([full_table_name] VARCHAR(128),[column_name] VARCHAR(128),[data_type] VARCHAR(20) NOT NULL,[key_type] VARCHAR(20),[clustering_order] VARCHAR(10),[indexed] BIT NOT NULL,[ordinal_position] INTEGER NOT NULL,PRIMARY KEY ([full_table_name], [column_name]))", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); - } - - @Test - public void - repairTable_WithMissingMetadataTableForSqlite_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.SQLITE, - "SELECT 1 FROM \"my_ns$foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "$metadata\" LIMIT 1", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "$metadata\"(\"full_table_name\" TEXT,\"column_name\" TEXT,\"data_type\" TEXT NOT NULL,\"key_type\" TEXT,\"clustering_order\" TEXT,\"indexed\" BOOLEAN NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,FALSE,1)"); - } - - @Test - public void - repairTable_WithMissingMetadataTableForDb2_shouldCreateMetadataTableAndAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine.DB2, - "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" LIMIT 1", - "CREATE SCHEMA \"" + tableMetadataSchemaName + "\"", - "CREATE TABLE IF NOT EXISTS \"" - + tableMetadataSchemaName - + "\".\"metadata\"(\"full_table_name\" VARCHAR(128) NOT NULL,\"column_name\" VARCHAR(128) NOT NULL,\"data_type\" VARCHAR(20) NOT NULL,\"key_type\" VARCHAR(20),\"clustering_order\" VARCHAR(10),\"indexed\" BOOLEAN NOT NULL,\"ordinal_position\" INTEGER NOT NULL,PRIMARY KEY (\"full_table_name\", \"column_name\"))", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=true WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } private void - repairTable_WithMissingMetadataTableForX_shouldCreateMetadataTableAndAddMetadataForTable( - RdbEngine rdbEngine, String... expectedSqlStatements) + createIndex_forColumnTypeWithRequiredAlterationForX_ShouldAlterColumnAndCreateIndexProperly( + RdbEngine rdbEngine, + String expectedGetTableMetadataStatement, + String[] expectedAlterColumnTypeStatements, + String expectedCreateIndexStatement, + String expectedUpdateTableMetadataStatement) throws SQLException, ExecutionException { // Arrange String namespace = "my_ns"; - String table = "foo_table"; - TableMetadata metadata = - TableMetadata.newBuilder().addPartitionKey("c1").addColumn("c1", DataType.TEXT).build(); - - List mockedStatements = new ArrayList<>(); - for (int i = 0; i < expectedSqlStatements.length; i++) { - mockedStatements.add(mock(Statement.class)); - } + String table = "my_tbl"; + String indexColumn = "my_column"; + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - when(connection.createStatement()) - .thenReturn( - mockedStatements.get(0), - mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); - when(dataSource.getConnection()).thenReturn(connection); + PreparedStatement selectStatement = mock(PreparedStatement.class); + ResultSet resultSet = + mockResultSet( + new SelectAllFromMetadataTableResultSetMocker.Row( + "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.TEXT.toString(), null, null, false)); + when(selectStatement.executeQuery()).thenReturn(resultSet); + when(connection.prepareStatement(any())).thenReturn(selectStatement); - // Mock that the metadata table does not exist - SQLException sqlException = mock(SQLException.class); - mockUndefinedTableError(rdbEngine, sqlException); - when(mockedStatements.get(1).execute(anyString())).thenThrow(sqlException); + Statement statement = mock(Statement.class); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + when(dataSource.getConnection()).thenReturn(connection); + when(connection.createStatement()).thenReturn(statement); // Act - admin.repairTable(namespace, table, metadata, new HashMap<>()); + admin.createIndex(namespace, table, indexColumn, Collections.emptyMap()); // Assert - for (int i = 0; i < expectedSqlStatements.length; i++) { - verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); + verify(connection).prepareStatement(expectedGetTableMetadataStatement); + verify(selectStatement).setString(1, getFullTableName(namespace, table)); + + verify(connection, times(2 + expectedAlterColumnTypeStatements.length)).createStatement(); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(statement, times(2 + expectedAlterColumnTypeStatements.length)) + .execute(captor.capture()); + for (int i = 0; i < expectedAlterColumnTypeStatements.length; i++) { + assertThat(captor.getAllValues().get(i)).isEqualTo(expectedAlterColumnTypeStatements[i]); } + assertThat(captor.getAllValues().get(expectedAlterColumnTypeStatements.length)) + .isEqualTo(expectedCreateIndexStatement); + assertThat(captor.getAllValues().get(expectedAlterColumnTypeStatements.length + 1)) + .isEqualTo(expectedUpdateTableMetadataStatement); } @Test - public void repairTable_ExistingMetadataTableForMysql_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( + public void dropIndex_forColumnTypeWithoutRequiredAlterationForMysql_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( RdbEngine.MYSQL, - "SELECT 1 FROM `my_ns`.`foo_table` LIMIT 1", - "SELECT 1 FROM `" + tableMetadataSchemaName + "`.`metadata` LIMIT 1", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", - "INSERT INTO `" - + tableMetadataSchemaName - + "`.`metadata` VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", + "DROP INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl`", + "UPDATE `" + + METADATA_SCHEMA + + "`.`metadata` SET `indexed`=false WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); } @Test - public void repairTable_ExistingMetadataTableForOracle_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( - RdbEngine.ORACLE, - "SELECT 1 FROM \"my_ns\".\"foo_table\" FETCH FIRST 1 ROWS ONLY", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" FETCH FIRST 1 ROWS ONLY", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); + public void + dropIndex_forColumnTypeWithoutRequiredAlterationForPostgresql_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.POSTGRESQL, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void repairTable_ExistingMetadataTableForPosgresql_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( - RdbEngine.POSTGRESQL, - "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" LIMIT 1", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + public void dropIndex_forColumnTypeWithoutRequiredAlterationForServer_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.SQL_SERVER, + "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", + "DROP INDEX [index_my_ns_my_tbl_my_column] ON [my_ns].[my_tbl]", + "UPDATE [" + + METADATA_SCHEMA + + "].[metadata] SET [indexed]=0 WHERE [full_table_name]='my_ns.my_tbl' AND [column_name]='my_column'"); } @Test - public void repairTable_ExistingMetadataTableForSqlServer_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( - RdbEngine.SQL_SERVER, - "SELECT TOP 1 1 FROM [my_ns].[foo_table]", - "SELECT TOP 1 1 FROM [" + tableMetadataSchemaName + "].[metadata]", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,0,1)"); + public void dropIndex_forColumnTypeWithoutRequiredAlterationForOracle_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.ORACLE, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=0 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void repairTable_ExistingMetadataTableForSqlite_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( + public void dropIndex_forColumnTypeWithoutRequiredAlterationForSqlite_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( RdbEngine.SQLITE, - "SELECT 1 FROM \"my_ns$foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "$metadata\" LIMIT 1", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,FALSE,1)"); + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"index_my_ns_my_tbl_my_column\"", + "UPDATE \"" + + METADATA_SCHEMA + + "$metadata\" SET \"indexed\"=FALSE WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void repairTable_ExistingMetadataTableForDb2_shouldDeleteThenAddMetadataForTable() - throws SQLException, ExecutionException { - repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( + public void dropIndex_forColumnTypeWithoutRequiredAlterationForDb2_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( RdbEngine.DB2, - "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1", - "SELECT 1 FROM \"" + tableMetadataSchemaName + "\".\"metadata\" LIMIT 1", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('my_ns.foo_table','c1','TEXT','PARTITION',NULL,false,1)"); + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } - private void repairTable_ExistingMetadataTableForX_shouldDeleteThenAddMetadataForTable( - RdbEngine rdbEngine, String... expectedSqlStatements) + private void dropIndex_forColumnTypeWithoutRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine rdbEngine, + String expectedGetTableMetadataStatement, + String expectedDropIndexStatement, + String expectedUpdateTableMetadataStatement) throws SQLException, ExecutionException { // Arrange String namespace = "my_ns"; - String table = "foo_table"; - TableMetadata metadata = - TableMetadata.newBuilder().addPartitionKey("c1").addColumn("c1", DataType.TEXT).build(); + String table = "my_tbl"; + String indexColumn = "my_column"; + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + PreparedStatement selectStatement = mock(PreparedStatement.class); + ResultSet resultSet = + mockResultSet( + new SelectAllFromMetadataTableResultSetMocker.Row( + "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.BOOLEAN.toString(), null, null, false)); + when(selectStatement.executeQuery()).thenReturn(resultSet); + when(connection.prepareStatement(any())).thenReturn(selectStatement); - List mockedStatements = new ArrayList<>(); - for (int i = 0; i < expectedSqlStatements.length; i++) { - mockedStatements.add(mock(Statement.class)); - } + Statement statement = mock(Statement.class); - when(connection.createStatement()) - .thenReturn( - mockedStatements.get(0), - mockedStatements.subList(1, mockedStatements.size()).toArray(new Statement[0])); when(dataSource.getConnection()).thenReturn(connection); - - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + when(connection.createStatement()).thenReturn(statement); // Act - admin.repairTable(namespace, table, metadata, new HashMap<>()); + admin.dropIndex(namespace, table, indexColumn); // Assert - for (int i = 0; i < expectedSqlStatements.length; i++) { - verify(mockedStatements.get(i)).execute(expectedSqlStatements[i]); - } - } + verify(connection).prepareStatement(expectedGetTableMetadataStatement); + verify(selectStatement).setString(1, getFullTableName(namespace, table)); - @Test - public void repairTable_WithNonExistingTableToRepairForMysql_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.MYSQL, "SELECT 1 FROM `my_ns`.`foo_table` LIMIT 1"); + verify(connection, times(2)).createStatement(); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(statement, times(2)).execute(captor.capture()); + assertThat(captor.getAllValues().get(0)).isEqualTo(expectedDropIndexStatement); + assertThat(captor.getAllValues().get(1)).isEqualTo(expectedUpdateTableMetadataStatement); } @Test - public void - repairTable_WithNonExistingTableToRepairForOracle_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.ORACLE, "SELECT 1 FROM \"my_ns\".\"foo_table\" FETCH FIRST 1 ROWS ONLY"); + public void dropIndex_forColumnTypeWithRequiredAlterationForMysql_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.MYSQL, + "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" + + METADATA_SCHEMA + + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", + "DROP INDEX `index_my_ns_my_tbl_my_column` ON `my_ns`.`my_tbl`", + new String[] {"ALTER TABLE `my_ns`.`my_tbl` MODIFY `my_column` LONGTEXT"}, + "UPDATE `" + + METADATA_SCHEMA + + "`.`metadata` SET `indexed`=false WHERE `full_table_name`='my_ns.my_tbl' AND `column_name`='my_column'"); } @Test - public void - repairTable_WithNonExistingTableToRepairForPostgresql_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.POSTGRESQL, "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1"); + public void dropIndex_forColumnTypeWithRequiredAlterationForPostgresql_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.POSTGRESQL, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" TYPE TEXT"}, + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void - repairTable_WithNonExistingTableToRepairForSqlServer_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.SQL_SERVER, "SELECT TOP 1 1 FROM [my_ns].[foo_table]"); + public void dropIndex_forColumnTypeWithRequiredAlterationForOracle_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.ORACLE, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + new String[] {"ALTER TABLE \"my_ns\".\"my_tbl\" MODIFY ( \"my_column\" VARCHAR2(4000) )"}, + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=0 WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } @Test - public void - repairTable_WithNonExistingTableToRepairForSqlite_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.SQLITE, "SELECT 1 FROM \"my_ns$foo_table\" LIMIT 1"); + public void dropIndex_forColumnTypeWithRequiredAlterationForSqlite_ShouldDropIndexProperly() { + // SQLite does not require column type change on CREATE INDEX. } @Test - public void repairTable_WithNonExistingTableToRepairForDb2_shouldThrowIllegalArgumentException() - throws SQLException { - repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine.DB2, "SELECT 1 FROM \"my_ns\".\"foo_table\" LIMIT 1"); + public void dropIndex_forColumnTypeWithRequiredAlterationForDb2_ShouldDropIndexProperly() + throws Exception { + dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine.DB2, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "DROP INDEX \"my_ns\".\"index_my_ns_my_tbl_my_column\"", + new String[] { + "ALTER TABLE \"my_ns\".\"my_tbl\" ALTER COLUMN \"my_column\" SET DATA TYPE VARCHAR(32672)", + "CALL SYSPROC.ADMIN_CMD('REORG TABLE \"my_ns\".\"my_tbl\"')" + }, + "UPDATE \"" + + METADATA_SCHEMA + + "\".\"metadata\" SET \"indexed\"=false WHERE \"full_table_name\"='my_ns.my_tbl' AND \"column_name\"='my_column'"); } - private void repairTable_WithNonExistingTableToRepairForX_shouldThrowIllegalArgumentException( - RdbEngine rdbEngine, String expectedCheckTableExistStatement) throws SQLException { + private void dropIndex_forColumnTypeWithRequiredAlterationForX_ShouldDropIndexProperly( + RdbEngine rdbEngine, + String expectedGetTableMetadataStatement, + String expectedDropIndexStatement, + String[] expectedAlterColumnStatements, + String expectedUpdateTableMetadataStatement) + throws SQLException, ExecutionException { // Arrange String namespace = "my_ns"; - String table = "foo_table"; - TableMetadata metadata = - TableMetadata.newBuilder().addPartitionKey("c1").addColumn("c1", DataType.TEXT).build(); + String table = "my_tbl"; + String indexColumn = "my_column"; + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + PreparedStatement selectStatement = mock(PreparedStatement.class); + ResultSet resultSet = + mockResultSet( + new SelectAllFromMetadataTableResultSetMocker.Row( + "c1", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.TEXT.toString(), null, null, false)); + when(selectStatement.executeQuery()).thenReturn(resultSet); + when(connection.prepareStatement(any())).thenReturn(selectStatement); - Statement checkTableExistStatement = mock(Statement.class); - when(connection.createStatement()).thenReturn(checkTableExistStatement); - when(dataSource.getConnection()).thenReturn(connection); + Statement statement = mock(Statement.class); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - SQLException sqlException = mock(SQLException.class); - mockUndefinedTableError(rdbEngine, sqlException); - when(checkTableExistStatement.execute(any())).thenThrow(sqlException); + when(dataSource.getConnection()).thenReturn(connection); + when(connection.createStatement()).thenReturn(statement); // Act - assertThatThrownBy(() -> admin.repairTable(namespace, table, metadata, new HashMap<>())) - .isInstanceOf(IllegalArgumentException.class); + admin.dropIndex(namespace, table, indexColumn); // Assert - verify(checkTableExistStatement).execute(expectedCheckTableExistStatement); + verify(connection).prepareStatement(expectedGetTableMetadataStatement); + verify(selectStatement).setString(1, getFullTableName(namespace, table)); + + verify(connection, times(2 + expectedAlterColumnStatements.length)).createStatement(); + ArgumentCaptor captor = ArgumentCaptor.forClass(String.class); + verify(statement, times(2 + expectedAlterColumnStatements.length)).execute(captor.capture()); + assertThat(captor.getAllValues().get(0)).isEqualTo(expectedDropIndexStatement); + for (int i = 0; i < expectedAlterColumnStatements.length; i++) { + assertThat(captor.getAllValues().get(i + 1)).isEqualTo(expectedAlterColumnStatements[i]); + } + assertThat(captor.getAllValues().get(expectedAlterColumnStatements.length + 1)) + .isEqualTo(expectedUpdateTableMetadataStatement); } @Test @@ -2727,17 +2759,15 @@ public void addNewColumnToTable_ForMysql_ShouldWorkProperly() addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", "ALTER TABLE `ns`.`table` ADD `c2` INT", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'ns.table'", + "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'ns.table'", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c2','INT',NULL,NULL,false,2)"); } @@ -2747,37 +2777,37 @@ public void addNewColumnToTable_ForOracle_ShouldWorkProperly() addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" ADD \"c2\" NUMBER(10)", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','INT',NULL,NULL,0,2)"); } @Test - public void addNewColumnToTable_ForPostgrsql_ShouldWorkProperly() + public void addNewColumnToTable_ForPostgresql_ShouldWorkProperly() throws SQLException, ExecutionException { addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" ADD \"c2\" INT", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','INT',NULL,NULL,false,2)"); } @@ -2787,17 +2817,15 @@ public void addNewColumnToTable_ForSqlServer_ShouldWorkProperly() addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", "ALTER TABLE [ns].[table] ADD [c2] INT", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'ns.table'", + "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'ns.table'", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c2','INT',NULL,NULL,0,2)"); } @@ -2807,17 +2835,15 @@ public void addNewColumnToTable_ForSqlite_ShouldWorkProperly() addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.SQLITE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns$table\" ADD \"c2\" INT", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", + "DELETE FROM \"" + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,FALSE,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" VALUES ('ns.table','c2','INT',NULL,NULL,FALSE,2)"); } @@ -2827,17 +2853,17 @@ public void addNewColumnToTable_ForDb2_ShouldWorkProperly() addNewColumnToTable_ForX_ShouldWorkProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" ADD \"c2\" INT", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','INT',NULL,NULL,false,2)"); } @@ -2850,15 +2876,14 @@ private void addNewColumnToTable_ForX_ShouldWorkProperly( String currentColumn = "c1"; String newColumn = "c2"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Collections.singletonList( - new Row(currentColumn, DataType.TEXT.toString(), "PARTITION", null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + currentColumn, DataType.TEXT.toString(), "PARTITION", null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); List expectedStatements = new ArrayList<>(); for (int i = 0; i < expectedSqlStatements.length; i++) { Statement expectedStatement = mock(Statement.class); @@ -2889,14 +2914,12 @@ public void dropColumnFromTable_ForMysql_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", "ALTER TABLE `ns`.`table` DROP COLUMN `c2`", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'ns.table'", + "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'ns.table'", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)"); } @@ -2906,14 +2929,14 @@ public void dropColumnFromTable_ForOracle_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" DROP COLUMN \"c2\"", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)"); } @@ -2923,14 +2946,14 @@ public void dropColumnFromTable_ForPostgresql_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" DROP COLUMN \"c2\"", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)"); } @@ -2940,14 +2963,12 @@ public void dropColumnFromTable_ForSqlServer_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", "ALTER TABLE [ns].[table] DROP COLUMN [c2]", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'ns.table'", + "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'ns.table'", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)"); } @@ -2957,14 +2978,12 @@ public void dropColumnFromTable_ForSqlite_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.SQLITE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns$table\" DROP COLUMN \"c2\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", + "DELETE FROM \"" + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,FALSE,1)"); } @@ -2974,15 +2993,15 @@ public void dropColumnFromTable_ForDb2_ShouldWorkProperly() dropColumnFromTable_ForX_ShouldWorkProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" DROP COLUMN \"c2\"", "CALL SYSPROC.ADMIN_CMD('REORG TABLE \"ns\".\"table\"')", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)"); } @@ -2995,16 +3014,16 @@ private void dropColumnFromTable_ForX_ShouldWorkProperly( String column1 = "c1"; String column2 = "c2"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new Row(column1, DataType.TEXT.toString(), "PARTITION", null, false), - new Row(column2, DataType.INT.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + column1, DataType.TEXT.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + column2, DataType.INT.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); List expectedStatements = new ArrayList<>(); for (int i = 0; i < expectedSqlStatements.length; i++) { Statement expectedStatement = mock(Statement.class); @@ -3034,17 +3053,15 @@ public void renameColumn_ForMysql_ShouldWorkProperly() throws SQLException, Exec renameColumn_ForX_ShouldWorkProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", "ALTER TABLE `ns`.`table` CHANGE COLUMN `c2` `c3` INT", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'ns.table'", + "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'ns.table'", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c3','INT',NULL,NULL,false,2)"); } @@ -3053,17 +3070,17 @@ public void renameColumn_ForOracle_ShouldWorkProperly() throws SQLException, Exe renameColumn_ForX_ShouldWorkProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" RENAME COLUMN \"c2\" TO \"c3\"", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c3','INT',NULL,NULL,0,2)"); } @@ -3073,17 +3090,17 @@ public void renameColumn_ForPostgresql_ShouldWorkProperly() renameColumn_ForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" RENAME COLUMN \"c2\" TO \"c3\"", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c3','INT',NULL,NULL,false,2)"); } @@ -3093,17 +3110,15 @@ public void renameColumn_ForSqlServer_ShouldWorkProperly() renameColumn_ForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", "EXEC sp_rename '[ns].[table].[c2]', 'c3', 'COLUMN'", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'ns.table'", + "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'ns.table'", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c3','INT',NULL,NULL,0,2)"); } @@ -3112,17 +3127,15 @@ public void renameColumn_ForSqlite_ShouldWorkProperly() throws SQLException, Exe renameColumn_ForX_ShouldWorkProperly( RdbEngine.SQLITE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns$table\" RENAME COLUMN \"c2\" TO \"c3\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", + "DELETE FROM \"" + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,FALSE,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "$metadata\" VALUES ('ns.table','c3','INT',NULL,NULL,FALSE,2)"); } @@ -3131,17 +3144,17 @@ public void renameColumn_ForDb2_ShouldWorkProperly() throws SQLException, Execut renameColumn_ForX_ShouldWorkProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" RENAME COLUMN \"c2\" TO \"c3\"", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c3','INT',NULL,NULL,false,2)"); } @@ -3151,20 +3164,20 @@ private void renameColumn_ForX_ShouldWorkProperly( // Arrange String namespace = "ns"; String table = "table"; - String column1 = "c1"; - String column2 = "c2"; - String column3 = "c3"; + String columnName1 = "c1"; + String columnName2 = "c2"; + String columnName3 = "c3"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new Row(column1, DataType.TEXT.toString(), "PARTITION", null, false), - new Row(column2, DataType.INT.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName1, DataType.TEXT.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName2, DataType.INT.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); List expectedStatements = new ArrayList<>(); for (int i = 0; i < expectedSqlStatements.length; i++) { Statement expectedStatement = mock(Statement.class); @@ -3179,7 +3192,7 @@ private void renameColumn_ForX_ShouldWorkProperly( JdbcAdmin admin = createJdbcAdminFor(rdbEngine); // Act - admin.renameColumn(namespace, table, column2, column3); + admin.renameColumn(namespace, table, columnName2, columnName3); // Assert verify(selectStatement).setString(1, getFullTableName(namespace, table)); @@ -3195,17 +3208,15 @@ public void alterColumnType_ForMysql_ShouldWorkProperly() alterColumnType_ForX_ShouldWorkProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", "ALTER TABLE `ns`.`table` MODIFY `c2` BIGINT", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'ns.table'", + "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'ns.table'", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table','c2','BIGINT',NULL,NULL,false,2)"); } @@ -3215,17 +3226,17 @@ public void alterColumnType_ForOracle_ShouldWorkProperly() alterColumnType_ForX_ShouldWorkProperly( RdbEngine.ORACLE, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" MODIFY ( \"c2\" NUMBER(16) )", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','BIGINT',NULL,NULL,0,2)"); } @@ -3235,17 +3246,17 @@ public void alterColumnType_ForPostgresql_ShouldWorkProperly() alterColumnType_ForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" ALTER COLUMN \"c2\" TYPE BIGINT", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','BIGINT',NULL,NULL,false,2)"); } @@ -3255,17 +3266,15 @@ public void alterColumnType_ForSqlServer_ShouldWorkProperly() alterColumnType_ForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", "ALTER TABLE [ns].[table] ALTER COLUMN [c2] BIGINT", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'ns.table'", + "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'ns.table'", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c1','TEXT','PARTITION',NULL,0,1)", "INSERT INTO [" - + tableMetadataSchemaName + + METADATA_SCHEMA + "].[metadata] VALUES ('ns.table','c2','BIGINT',NULL,NULL,0,2)"); } @@ -3278,16 +3287,15 @@ public void alterColumnType_ForSqlite_ShouldThrowUnsupportedOperationException() String columnName1 = "c1"; String columnName2 = "c2"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new Row(columnName1, DataType.TEXT.toString(), "PARTITION", null, false), - new Row(columnName2, DataType.INT.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName1, DataType.TEXT.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName2, DataType.INT.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); when(dataSource.getConnection()).thenReturn(connection); JdbcAdmin admin = createJdbcAdminFor(RdbEngine.SQLITE); @@ -3302,18 +3310,18 @@ public void alterColumnType_ForDb2_ShouldWorkProperly() throws SQLException, Exe alterColumnType_ForX_ShouldWorkProperly( RdbEngine.DB2, "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", "ALTER TABLE \"ns\".\"table\" ALTER COLUMN \"c2\" SET DATA TYPE BIGINT", "CALL SYSPROC.ADMIN_CMD('REORG TABLE \"ns\".\"table\"')", "DELETE FROM \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO \"" - + tableMetadataSchemaName + + METADATA_SCHEMA + "\".\"metadata\" VALUES ('ns.table','c2','BIGINT',NULL,NULL,false,2)"); } @@ -3326,16 +3334,16 @@ private void alterColumnType_ForX_ShouldWorkProperly( String columnName1 = "c1"; String columnName2 = "c2"; - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new Row(columnName1, DataType.TEXT.toString(), "PARTITION", null, false), - new Row(columnName2, DataType.INT.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName1, DataType.TEXT.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName2, DataType.INT.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); List expectedStatements = new ArrayList<>(); for (int i = 0; i < expectedSqlStatements.length; i++) { Statement expectedStatement = mock(Statement.class); @@ -3365,167 +3373,312 @@ public void renameTable_ForMysql_ShouldWorkProperly() throws SQLException, Execu renameTable_ForX_ShouldWorkProperly( RdbEngine.MYSQL, "SELECT `column_name`,`data_type`,`key_type`,`clustering_order`,`indexed` FROM `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name`=? ORDER BY `ordinal_position` ASC", "ALTER TABLE `ns`.`table` RENAME TO `ns`.`table_new`", - "DELETE FROM `" - + tableMetadataSchemaName - + "`.`metadata` WHERE `full_table_name` = 'ns.table'", + "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'ns.table'", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,false,1)", "INSERT INTO `" - + tableMetadataSchemaName + + METADATA_SCHEMA + "`.`metadata` VALUES ('ns.table_new','c2','INT',NULL,NULL,false,2)"); } @Test - public void renameTable_ForOracle_ShouldWorkProperly() throws SQLException, ExecutionException { - renameTable_ForX_ShouldWorkProperly( - RdbEngine.ORACLE, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "ALTER TABLE \"ns\".\"table\" RENAME TO \"table_new\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,0,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,0,2)"); + public void renameTable_ForOracle_ShouldWorkProperly() throws SQLException, ExecutionException { + renameTable_ForX_ShouldWorkProperly( + RdbEngine.ORACLE, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "ALTER TABLE \"ns\".\"table\" RENAME TO \"table_new\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,0,2)"); + } + + @Test + public void renameTable_ForPostgresql_ShouldWorkProperly() + throws SQLException, ExecutionException { + renameTable_ForX_ShouldWorkProperly( + RdbEngine.POSTGRESQL, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "ALTER TABLE \"ns\".\"table\" RENAME TO \"table_new\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,false,2)"); + } + + @Test + public void renameTable_ForSqlServer_ShouldWorkProperly() + throws SQLException, ExecutionException { + renameTable_ForX_ShouldWorkProperly( + RdbEngine.SQL_SERVER, + "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" + + METADATA_SCHEMA + + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", + "EXEC sp_rename '[ns].[table]', 'table_new'", + "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'ns.table'", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,0,1)", + "INSERT INTO [" + + METADATA_SCHEMA + + "].[metadata] VALUES ('ns.table_new','c2','INT',NULL,NULL,0,2)"); + } + + @Test + public void renameTable_ForSqlite_ShouldWorkProperly() throws SQLException, ExecutionException { + renameTable_ForX_ShouldWorkProperly( + RdbEngine.SQLITE, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "ALTER TABLE \"ns$table\" RENAME TO \"ns$table_new\"", + "DELETE FROM \"" + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,FALSE,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "$metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,FALSE,2)"); + } + + @Test + public void renameTable_ForDb2_ShouldWorkProperly() throws SQLException, ExecutionException { + renameTable_ForX_ShouldWorkProperly( + RdbEngine.DB2, + "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", + "RENAME \"ns\".\"table\" TO \"table_new\"", + "DELETE FROM \"" + + METADATA_SCHEMA + + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,false,1)", + "INSERT INTO \"" + + METADATA_SCHEMA + + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,false,2)"); + } + + private void renameTable_ForX_ShouldWorkProperly( + RdbEngine rdbEngine, String expectedGetMetadataStatement, String... expectedSqlStatements) + throws SQLException, ExecutionException { + // Arrange + String namespace = "ns"; + String table = "table"; + String columnName1 = "c1"; + String columnName2 = "c2"; + + PreparedStatement selectStatement = mock(PreparedStatement.class); + ResultSet resultSet1 = + mockResultSet( + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName1, DataType.TEXT.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + columnName2, DataType.INT.toString(), null, null, false)); + when(selectStatement.executeQuery()).thenReturn(resultSet1); + when(connection.prepareStatement(any())).thenReturn(selectStatement); + List expectedStatements = new ArrayList<>(); + for (String expectedSqlStatement : expectedSqlStatements) { + Statement mock = mock(Statement.class); + expectedStatements.add(mock); + if (expectedSqlStatement.startsWith("SELECT DISTINCT ")) { + ResultSet resultSet2 = mock(ResultSet.class); + when(resultSet2.next()).thenReturn(true); + when(mock.executeQuery(any())).thenReturn(resultSet2); + } + } + when(connection.createStatement()) + .thenReturn( + expectedStatements.get(0), + expectedStatements.subList(1, expectedStatements.size()).toArray(new Statement[0])); + + when(dataSource.getConnection()).thenReturn(connection); + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + admin.renameTable(namespace, table, "table_new"); + + // Assert + verify(selectStatement).setString(1, getFullTableName(namespace, table)); + verify(connection).prepareStatement(expectedGetMetadataStatement); + for (int i = 0; i < expectedSqlStatements.length; i++) { + if (expectedSqlStatements[i].startsWith("SELECT DISTINCT ")) { + verify(expectedStatements.get(i)).executeQuery(expectedSqlStatements[i]); + } else { + verify(expectedStatements.get(i)).execute(expectedSqlStatements[i]); + } + } + } + + @Test + public void getNamespaceNames_ForMysql_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.MYSQL, + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`"); + } + + @Test + public void getNamespaceNames_Posgresql_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.POSTGRESQL, + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void getNamespaceNames_Oracle_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.ORACLE, + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void getNamespaceNames_SqlServer_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.SQL_SERVER, + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]"); + } + + @Test + public void getNamespaceNames_Sqlite_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.SQLITE, + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\""); + } + + @Test + public void getNamespaceNames_Db2_WithExistingTables_ShouldWorkProperly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine.DB2, + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + private void getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( + RdbEngine rdbEngine, String getTableMetadataNamespacesStatement) + throws SQLException, ExecutionException { + // Arrange + Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); + + when(connection.createStatement()).thenReturn(getTableMetadataNamespacesStatementMock); + when(dataSource.getConnection()).thenReturn(connection); + + ResultSet resultSet = + mockResultSet( + new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns1.tbl1"), + new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns1.tbl2"), + new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns2.tbl3")); + when(getTableMetadataNamespacesStatementMock.executeQuery(anyString())).thenReturn(resultSet); + JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + + // Act + Set actual = admin.getNamespaceNames(); + + // Assert + if (rdbEngine == RdbEngine.MYSQL || rdbEngine == RdbEngine.SQLITE) { + verify(connection, never()).setReadOnly(anyBoolean()); + } else { + verify(connection).setReadOnly(true); + } + verify(connection).createStatement(); + verify(getTableMetadataNamespacesStatementMock) + .executeQuery(getTableMetadataNamespacesStatement); + assertThat(actual).containsOnly("ns1", "ns2", METADATA_SCHEMA); + } + + @Test + public void getNamespaceNames_ForMysql_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( + RdbEngine.MYSQL, + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`"); } @Test - public void renameTable_ForPostgresql_ShouldWorkProperly() + public void getNamespaceNames_Posgresql_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() throws SQLException, ExecutionException { - renameTable_ForX_ShouldWorkProperly( + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( RdbEngine.POSTGRESQL, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "ALTER TABLE \"ns\".\"table\" RENAME TO \"table_new\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,false,2)"); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); } @Test - public void renameTable_ForSqlServer_ShouldWorkProperly() + public void getNamespaceNames_Oracle_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() throws SQLException, ExecutionException { - renameTable_ForX_ShouldWorkProperly( + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( + RdbEngine.ORACLE, + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); + } + + @Test + public void getNamespaceNames_SqlServer_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( RdbEngine.SQL_SERVER, - "SELECT [column_name],[data_type],[key_type],[clustering_order],[indexed] FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name]=? ORDER BY [ordinal_position] ASC", - "EXEC sp_rename '[ns].[table]', 'table_new'", - "DELETE FROM [" - + tableMetadataSchemaName - + "].[metadata] WHERE [full_table_name] = 'ns.table'", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,0,1)", - "INSERT INTO [" - + tableMetadataSchemaName - + "].[metadata] VALUES ('ns.table_new','c2','INT',NULL,NULL,0,2)"); + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]"); } @Test - public void renameTable_ForSqlite_ShouldWorkProperly() throws SQLException, ExecutionException { - renameTable_ForX_ShouldWorkProperly( + public void getNamespaceNames_Sqlite_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( RdbEngine.SQLITE, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "ALTER TABLE \"ns$table\" RENAME TO \"ns$table_new\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "$metadata\" WHERE \"full_table_name\" = 'ns.table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,FALSE,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "$metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,FALSE,2)"); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\""); } @Test - public void renameTable_ForDb2_ShouldWorkProperly() throws SQLException, ExecutionException { - renameTable_ForX_ShouldWorkProperly( + public void getNamespaceNames_Db2_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() + throws SQLException, ExecutionException { + getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( RdbEngine.DB2, - "SELECT \"column_name\",\"data_type\",\"key_type\",\"clustering_order\",\"indexed\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\"=? ORDER BY \"ordinal_position\" ASC", - "RENAME \"ns\".\"table\" TO \"table_new\"", - "DELETE FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\" WHERE \"full_table_name\" = 'ns.table'", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c1','TEXT','PARTITION',NULL,false,1)", - "INSERT INTO \"" - + tableMetadataSchemaName - + "\".\"metadata\" VALUES ('ns.table_new','c2','INT',NULL,NULL,false,2)"); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); } - private void renameTable_ForX_ShouldWorkProperly( - RdbEngine rdbEngine, String expectedGetMetadataStatement, String... expectedSqlStatements) + private void getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( + RdbEngine rdbEngine, String getTableMetadataNamespacesStatement) throws SQLException, ExecutionException { // Arrange - String namespace = "ns"; - String table = "table"; - String column1 = "c1"; - String column2 = "c2"; - - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); - PreparedStatement selectStatement = mock(PreparedStatement.class); - ResultSet resultSet1 = - mockResultSet( - Arrays.asList( - new Row(column1, DataType.TEXT.toString(), "PARTITION", null, false), - new Row(column2, DataType.INT.toString(), null, null, false))); - when(selectStatement.executeQuery()).thenReturn(resultSet1); - - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); - List expectedStatements = new ArrayList<>(); - for (String expectedSqlStatement : expectedSqlStatements) { - Statement mock = mock(Statement.class); - expectedStatements.add(mock); - if (expectedSqlStatement.startsWith("SELECT DISTINCT ")) { - ResultSet resultSet2 = mock(ResultSet.class); - when(resultSet2.next()).thenReturn(true); - when(mock.executeQuery(any())).thenReturn(resultSet2); - } - } - when(connection.createStatement()) - .thenReturn( - expectedStatements.get(0), - expectedStatements.subList(1, expectedStatements.size()).toArray(new Statement[0])); + Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); + when(connection.createStatement()).thenReturn(getTableMetadataNamespacesStatementMock); when(dataSource.getConnection()).thenReturn(connection); + + SQLException sqlException = mock(SQLException.class); + mockUndefinedTableError(rdbEngine, sqlException); + when(getTableMetadataNamespacesStatementMock.executeQuery(anyString())).thenThrow(sqlException); JdbcAdmin admin = createJdbcAdminFor(rdbEngine); // Act - admin.renameTable(namespace, table, "table_new"); + Set actual = admin.getNamespaceNames(); // Assert - verify(selectStatement).setString(1, getFullTableName(namespace, table)); - verify(connection).prepareStatement(expectedGetMetadataStatement); - for (int i = 0; i < expectedSqlStatements.length; i++) { - if (expectedSqlStatements[i].startsWith("SELECT DISTINCT ")) { - verify(expectedStatements.get(i)).executeQuery(expectedSqlStatements[i]); - } else { - verify(expectedStatements.get(i)).execute(expectedSqlStatements[i]); - } - } + verify(connection).createStatement(); + verify(getTableMetadataNamespacesStatementMock) + .executeQuery(getTableMetadataNamespacesStatement); + assertThat(actual).containsOnly(METADATA_SCHEMA); } @ParameterizedTest @@ -3776,14 +3929,15 @@ public void importTable_ForXBesidesSqlite_ShouldWorkProperly(RdbEngine rdbEngine .getImportTableMetadata(anyString(), anyString(), anyMap()); doNothing() .when(adminSpy) - .addTableMetadata(any(), anyString(), anyString(), any(), anyBoolean()); + .addTableMetadata(any(), anyString(), anyString(), any(), anyBoolean(), anyBoolean()); // Act adminSpy.importTable(NAMESPACE, TABLE, Collections.emptyMap(), Collections.emptyMap()); // Assert verify(adminSpy).getImportTableMetadata(NAMESPACE, TABLE, Collections.emptyMap()); - verify(adminSpy).addTableMetadata(connection, NAMESPACE, TABLE, importedTableMetadata, true); + verify(adminSpy) + .addTableMetadata(connection, NAMESPACE, TABLE, importedTableMetadata, true, false); } @Test @@ -3799,241 +3953,106 @@ public void importTable_ForSQLite_ShouldThrowUnsupportedOperationException() { assertThat(thrown).isInstanceOf(UnsupportedOperationException.class); } - @Test - void hasDifferentClusteringOrders_GivenOnlyAscOrders_ShouldReturnFalse() { - // Arrange - TableMetadata metadata = - TableMetadata.newBuilder() - .addPartitionKey("pk") - .addClusteringKey("ck1", Order.ASC) - .addClusteringKey("ck2", Order.ASC) - .addColumn("pk", DataType.INT) - .addColumn("ck1", DataType.INT) - .addColumn("ck2", DataType.INT) - .addColumn("value", DataType.TEXT) - .build(); - - // Act - // Assert - assertThat(hasDifferentClusteringOrders(metadata)).isFalse(); - } - - @Test - void hasDifferentClusteringOrders_GivenOnlyDescOrders_ShouldReturnFalse() { - // Arrange - TableMetadata metadata = - TableMetadata.newBuilder() - .addPartitionKey("pk") - .addClusteringKey("ck1", Order.DESC) - .addClusteringKey("ck2", Order.DESC) - .addColumn("pk", DataType.INT) - .addColumn("ck1", DataType.INT) - .addColumn("ck2", DataType.INT) - .addColumn("value", DataType.TEXT) - .build(); - - // Act - // Assert - assertThat(hasDifferentClusteringOrders(metadata)).isFalse(); - } - - @Test - void hasDifferentClusteringOrders_GivenBothAscAndDescOrders_ShouldReturnTrue() { - // Arrange - TableMetadata metadata1 = - TableMetadata.newBuilder() - .addPartitionKey("pk") - .addClusteringKey("ck1", Order.ASC) - .addClusteringKey("ck2", Order.DESC) - .addColumn("pk", DataType.INT) - .addColumn("ck1", DataType.INT) - .addColumn("ck2", DataType.INT) - .addColumn("value", DataType.TEXT) - .build(); - TableMetadata metadata2 = - TableMetadata.newBuilder() - .addPartitionKey("pk") - .addClusteringKey("ck1", Order.DESC) - .addClusteringKey("ck2", Order.ASC) - .addColumn("pk", DataType.INT) - .addColumn("ck1", DataType.INT) - .addColumn("ck2", DataType.INT) - .addColumn("value", DataType.TEXT) - .build(); - - // Act - // Assert - assertThat(hasDifferentClusteringOrders(metadata1)).isTrue(); - assertThat(hasDifferentClusteringOrders(metadata2)).isTrue(); - } - - @Test - public void getNamespaceNames_ForMysql_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.MYSQL, - "SELECT DISTINCT `full_table_name` FROM `" + tableMetadataSchemaName + "`.`metadata`"); - } - - @Test - public void getNamespaceNames_Posgresql_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.POSTGRESQL, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); - } - - @Test - public void getNamespaceNames_Oracle_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.ORACLE, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); - } - - @Test - public void getNamespaceNames_SqlServer_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.SQL_SERVER, - "SELECT DISTINCT [full_table_name] FROM [" + tableMetadataSchemaName + "].[metadata]"); - } - - @Test - public void getNamespaceNames_Sqlite_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.SQLITE, - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "$metadata\""); - } - - @Test - public void getNamespaceNames_Db2_WithExistingTables_ShouldWorkProperly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine.DB2, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); - } - - private void getNamespaceNames_ForX_WithExistingTables_ShouldWorkProperly( - RdbEngine rdbEngine, String getTableMetadataNamespacesStatement) - throws SQLException, ExecutionException { - // Arrange - Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); - - when(connection.createStatement()).thenReturn(getTableMetadataNamespacesStatementMock); - when(dataSource.getConnection()).thenReturn(connection); - - ResultSet resultSet = - mockResultSet( - new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns1.tbl1"), - new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns1.tbl2"), - new SelectFullTableNameFromMetadataTableResultSetMocker.Row("ns2.tbl3")); - when(getTableMetadataNamespacesStatementMock.executeQuery(anyString())).thenReturn(resultSet); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); - - // Act - Set actual = admin.getNamespaceNames(); + private String prepareSqlForTableCheck(RdbEngine rdbEngine, String namespace, String table) { + RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); + StringBuilder sql = + new StringBuilder("SELECT ") + .append(rdbEngine.equals(RdbEngine.SQL_SERVER) ? "TOP 1 1" : "1") + .append(" FROM ") + .append(rdbEngineStrategy.encloseFullTableName(namespace, table)); - // Assert - if (rdbEngine == RdbEngine.MYSQL || rdbEngine == RdbEngine.SQLITE) { - verify(connection, never()).setReadOnly(anyBoolean()); - } else { - verify(connection).setReadOnly(true); + switch (rdbEngine) { + case ORACLE: + sql.append(" FETCH FIRST 1 ROWS ONLY"); + break; + case SQL_SERVER: + break; + default: + sql.append(" LIMIT 1"); } - verify(connection).createStatement(); - verify(getTableMetadataNamespacesStatementMock) - .executeQuery(getTableMetadataNamespacesStatement); - assertThat(actual).containsOnly("ns1", "ns2", tableMetadataSchemaName); - } - @Test - public void getNamespaceNames_ForMysql_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.MYSQL, - "SELECT DISTINCT `full_table_name` FROM `" + tableMetadataSchemaName + "`.`metadata`"); + return sql.toString(); } - @Test - public void getNamespaceNames_Posgresql_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.POSTGRESQL, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); + private RdbEngineStrategy getRdbEngineStrategy(RdbEngine rdbEngine) { + RdbEngineStrategy engine = RDB_ENGINES.get(rdbEngine); + if (engine == null) { + throw new AssertionError("RdbEngineStrategy not found for " + rdbEngine); + } + return engine; } @Test - public void getNamespaceNames_Oracle_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.ORACLE, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); - } + void hasDifferentClusteringOrders_GivenOnlyAscOrders_ShouldReturnFalse() { + // Arrange + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("pk") + .addClusteringKey("ck1", Order.ASC) + .addClusteringKey("ck2", Order.ASC) + .addColumn("pk", DataType.INT) + .addColumn("ck1", DataType.INT) + .addColumn("ck2", DataType.INT) + .addColumn("value", DataType.TEXT) + .build(); - @Test - public void getNamespaceNames_SqlServer_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.SQL_SERVER, - "SELECT DISTINCT [full_table_name] FROM [" + tableMetadataSchemaName + "].[metadata]"); + // Act + // Assert + assertThat(hasDifferentClusteringOrders(metadata)).isFalse(); } @Test - public void getNamespaceNames_Sqlite_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.SQLITE, - "SELECT DISTINCT \"full_table_name\" FROM \"" + tableMetadataSchemaName + "$metadata\""); - } + void hasDifferentClusteringOrders_GivenOnlyDescOrders_ShouldReturnFalse() { + // Arrange + TableMetadata metadata = + TableMetadata.newBuilder() + .addPartitionKey("pk") + .addClusteringKey("ck1", Order.DESC) + .addClusteringKey("ck2", Order.DESC) + .addColumn("pk", DataType.INT) + .addColumn("ck1", DataType.INT) + .addColumn("ck2", DataType.INT) + .addColumn("value", DataType.TEXT) + .build(); - @Test - public void getNamespaceNames_Db2_WithoutExistingTables_ShouldReturnMetadataSchemaOnly() - throws SQLException, ExecutionException { - getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine.DB2, - "SELECT DISTINCT \"full_table_name\" FROM \"" - + tableMetadataSchemaName - + "\".\"metadata\""); + // Act + // Assert + assertThat(hasDifferentClusteringOrders(metadata)).isFalse(); } - private void getNamespaceNames_ForX_WithoutExistingTables_ShouldReturnMetadataSchemaOnly( - RdbEngine rdbEngine, String getTableMetadataNamespacesStatement) - throws SQLException, ExecutionException { + @Test + void hasDifferentClusteringOrders_GivenBothAscAndDescOrders_ShouldReturnTrue() { // Arrange - Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); - - when(connection.createStatement()).thenReturn(getTableMetadataNamespacesStatementMock); - when(dataSource.getConnection()).thenReturn(connection); - - SQLException sqlException = mock(SQLException.class); - mockUndefinedTableError(rdbEngine, sqlException); - when(getTableMetadataNamespacesStatementMock.executeQuery(anyString())).thenThrow(sqlException); - JdbcAdmin admin = createJdbcAdminFor(rdbEngine); + TableMetadata metadata1 = + TableMetadata.newBuilder() + .addPartitionKey("pk") + .addClusteringKey("ck1", Order.ASC) + .addClusteringKey("ck2", Order.DESC) + .addColumn("pk", DataType.INT) + .addColumn("ck1", DataType.INT) + .addColumn("ck2", DataType.INT) + .addColumn("value", DataType.TEXT) + .build(); + TableMetadata metadata2 = + TableMetadata.newBuilder() + .addPartitionKey("pk") + .addClusteringKey("ck1", Order.DESC) + .addClusteringKey("ck2", Order.ASC) + .addColumn("pk", DataType.INT) + .addColumn("ck1", DataType.INT) + .addColumn("ck2", DataType.INT) + .addColumn("value", DataType.TEXT) + .build(); // Act - Set actual = admin.getNamespaceNames(); - // Assert - verify(connection).createStatement(); - verify(getTableMetadataNamespacesStatementMock) - .executeQuery(getTableMetadataNamespacesStatement); - assertThat(actual).containsOnly(tableMetadataSchemaName); + assertThat(hasDifferentClusteringOrders(metadata1)).isTrue(); + assertThat(hasDifferentClusteringOrders(metadata2)).isTrue(); } @Test - void createTable_Db2_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationException() - throws SQLException { + void + createTableInternal_Db2_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationException() { // Arrange TableMetadata metadata1 = TableMetadata.newBuilder().addPartitionKey("pk").addColumn("pk", DataType.BLOB).build(); @@ -4052,17 +4071,15 @@ void createTable_Db2_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationE .addSecondaryIndex("col") .build(); JdbcAdmin admin = createJdbcAdminFor(RdbEngine.DB2); - when(connection.createStatement()).thenReturn(mock(Statement.class)); - when(dataSource.getConnection()).thenReturn(connection); // Act Assert - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata1, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata1, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "key"); - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata2, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata2, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "key"); - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata3, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata3, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "index"); } @@ -4076,17 +4093,15 @@ void createIndex_Db2_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationE String indexColumn = "index_col"; JdbcAdmin admin = createJdbcAdminFor(RdbEngine.DB2); - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "pk", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.BLOB.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + "pk", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.BLOB.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); Statement statement = mock(Statement.class); when(dataSource.getConnection()).thenReturn(connection); @@ -4100,8 +4115,8 @@ void createIndex_Db2_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationE } @Test - void createTable_Oracle_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationException() - throws SQLException { + void + createTableInternal_Oracle_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperationException() { // Arrange TableMetadata metadata1 = TableMetadata.newBuilder().addPartitionKey("pk").addColumn("pk", DataType.BLOB).build(); @@ -4120,17 +4135,15 @@ void createTable_Oracle_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperati .addSecondaryIndex("col") .build(); JdbcAdmin admin = createJdbcAdminFor(RdbEngine.ORACLE); - when(connection.createStatement()).thenReturn(mock(Statement.class)); - when(dataSource.getConnection()).thenReturn(connection); // Act Assert - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata1, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata1, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "key"); - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata2, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata2, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "key"); - assertThatThrownBy(() -> admin.createTable("ns", "tbl", metadata3, false)) + assertThatThrownBy(() -> admin.createTableInternal(connection, "ns", "tbl", metadata3, false)) .isInstanceOf(UnsupportedOperationException.class) .hasMessageContainingAll("BLOB", "index"); } @@ -4144,17 +4157,15 @@ void createIndex_Oracle_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperati String indexColumn = "index_col"; JdbcAdmin admin = createJdbcAdminFor(RdbEngine.ORACLE); - PreparedStatement checkStatement = prepareStatementForNamespaceCheck(); PreparedStatement selectStatement = mock(PreparedStatement.class); ResultSet resultSet = mockResultSet( - Arrays.asList( - new GetColumnsResultSetMocker.Row( - "pk", DataType.BOOLEAN.toString(), "PARTITION", null, false), - new GetColumnsResultSetMocker.Row( - indexColumn, DataType.BLOB.toString(), null, null, false))); + new SelectAllFromMetadataTableResultSetMocker.Row( + "pk", DataType.BOOLEAN.toString(), "PARTITION", null, false), + new SelectAllFromMetadataTableResultSetMocker.Row( + indexColumn, DataType.BLOB.toString(), null, null, false)); when(selectStatement.executeQuery()).thenReturn(resultSet); - when(connection.prepareStatement(any())).thenReturn(checkStatement).thenReturn(selectStatement); + when(connection.prepareStatement(any())).thenReturn(selectStatement); Statement statement = mock(Statement.class); when(dataSource.getConnection()).thenReturn(connection); @@ -4167,211 +4178,15 @@ void createIndex_Oracle_WithBlobColumnAsKeyOrIndex_ShouldThrowUnsupportedOperati .hasMessageContainingAll("BLOB", "index"); } - private PreparedStatement prepareStatementForNamespaceCheck() throws SQLException { - return prepareStatementForNamespaceCheck(true); - } - - private PreparedStatement prepareStatementForNamespaceCheck(boolean exists) throws SQLException { - PreparedStatement statement = mock(PreparedStatement.class); - ResultSet results = mock(ResultSet.class); - doNothing().when(statement).setString(anyInt(), anyString()); - when(statement.executeQuery()).thenReturn(results); - when(results.next()).thenReturn(exists); - return statement; - } - - private String prepareSqlForMetadataTableCheck(RdbEngine rdbEngine) { - return prepareSqlForTableCheck(rdbEngine, tableMetadataSchemaName, "metadata"); - } - - private String prepareSqlForTableCheck(RdbEngine rdbEngine, String namespace, String table) { - RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); - StringBuilder sql = - new StringBuilder("SELECT ") - .append(rdbEngine.equals(RdbEngine.SQL_SERVER) ? "TOP 1 1" : "1") - .append(" FROM ") - .append(rdbEngineStrategy.encloseFullTableName(namespace, table)); - - switch (rdbEngine) { - case ORACLE: - sql.append(" FETCH FIRST 1 ROWS ONLY"); - break; - case SQL_SERVER: - break; - default: - sql.append(" LIMIT 1"); - } - - return sql.toString(); - } - - private String prepareSqlForAlterTableAddColumn(RdbEngine rdbEngine, String column) { - RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); - String intType; - if (rdbEngineStrategy instanceof RdbEngineOracle) { - intType = "NUMBER(10)"; - } else { - intType = "INT"; - } - return "ALTER TABLE " - + rdbEngineStrategy.encloseFullTableName(NAMESPACE, TABLE) - + " ADD " - + rdbEngineStrategy.enclose(column) - + " " - + intType; - } - - private List prepareSqlForCreateSchemaStatements(RdbEngine rdbEngine) { - RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); - List statements = new ArrayList<>(); - - switch (rdbEngine) { - case MYSQL: - case POSTGRESQL: - case YUGABYTE: - case SQL_SERVER: - case MARIADB: - case DB2: - statements.add( - "CREATE SCHEMA " - + (rdbEngine == RdbEngine.SQL_SERVER || rdbEngine == RdbEngine.DB2 - ? "" - : "IF NOT EXISTS ") - + rdbEngineStrategy.enclose(tableMetadataSchemaName)); - break; - case ORACLE: - statements.add( - "CREATE USER " - + rdbEngineStrategy.enclose(tableMetadataSchemaName) - + " IDENTIFIED BY " - + rdbEngineStrategy.enclose("Oracle1234!@#$")); - statements.add( - "ALTER USER " - + rdbEngineStrategy.enclose(tableMetadataSchemaName) - + " quota unlimited on USERS"); - break; - default: - throw new AssertionError("Unsupported RDB engine: " + rdbEngine); - } - - return statements; - } - - private String prepareSqlForCreateMetadataTable(RdbEngine rdbEngine) { - RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); - StringBuilder sql = new StringBuilder("CREATE TABLE "); - if (!(rdbEngine.equals(RdbEngine.ORACLE) || rdbEngine.equals(RdbEngine.SQL_SERVER))) { - sql.append("IF NOT EXISTS "); - } - - sql.append(rdbEngineStrategy.encloseFullTableName(tableMetadataSchemaName, "metadata")) - .append("(") - .append(rdbEngineStrategy.enclose("full_table_name")) - .append(" ") - .append(getVarcharString(rdbEngine, 128)) - .append(rdbEngine == RdbEngine.DB2 ? " NOT NULL" : "") - .append(",") - .append(rdbEngineStrategy.enclose("column_name")) - .append(" ") - .append(getVarcharString(rdbEngine, 128)) - .append(rdbEngine == RdbEngine.DB2 ? " NOT NULL" : "") - .append(",") - .append(rdbEngineStrategy.enclose("data_type")) - .append(" ") - .append(getVarcharString(rdbEngine, 20)) - .append(" NOT NULL,") - .append(rdbEngineStrategy.enclose("key_type")) - .append(" ") - .append(getVarcharString(rdbEngine, 20)) - .append(",") - .append(rdbEngineStrategy.enclose("clustering_order")) - .append(" ") - .append(getVarcharString(rdbEngine, 10)) - .append(",") - .append(rdbEngineStrategy.enclose("indexed")); - - switch (rdbEngine) { - case ORACLE: - sql.append(" NUMBER(1) NOT NULL,"); - break; - case SQL_SERVER: - sql.append(" BIT NOT NULL,"); - break; - default: - sql.append(" BOOLEAN NOT NULL,"); - break; - } - - sql.append(rdbEngineStrategy.enclose("ordinal_position")) - .append(" INTEGER NOT NULL,PRIMARY KEY (") - .append(rdbEngineStrategy.enclose("full_table_name")) - .append(", ") - .append(rdbEngineStrategy.enclose("column_name")) - .append("))"); - - return sql.toString(); - } - - private String getVarcharString(RdbEngine rdbEngine, int size) { - switch (rdbEngine) { - case ORACLE: - return "VARCHAR2(" + size + ")"; - case SQLITE: - return "TEXT"; - default: - return "VARCHAR(" + size + ")"; - } - } - - private String prepareSqlForInsertMetadata( - RdbEngine rdbEngine, - String column, - String dataType, - String keyType, - String clusteringOrder, - boolean indexed, - int ordinal) { - RdbEngineStrategy rdbEngineStrategy = getRdbEngineStrategy(rdbEngine); - List values = - ImmutableList.of( - "'" + NAMESPACE + "." + TABLE + "'", - "'" + column + "'", - "'" + dataType + "'", - "'" + keyType + "'", - clusteringOrder, - getBooleanString(rdbEngine, indexed), - Integer.toString(ordinal)); - - return "INSERT INTO " - + rdbEngineStrategy.encloseFullTableName(tableMetadataSchemaName, "metadata") - + " VALUES (" - + String.join(",", values) - + ")"; - } - - private String getBooleanString(RdbEngine rdbEngine, boolean value) { - switch (rdbEngine) { - case ORACLE: - case SQL_SERVER: - return value ? "1" : "0"; - case SQLITE: - return value ? "TRUE" : "FALSE"; - default: - return value ? "true" : "false"; - } - } - - private RdbEngineStrategy getRdbEngineStrategy(RdbEngine rdbEngine) { - return RDB_ENGINES.getOrDefault(rdbEngine, new RdbEngineMysql()); - } - - // Utility class used to mock ResultSet for getTableMetadata test - static class GetColumnsResultSetMocker implements org.mockito.stubbing.Answer { + // Utility class used to mock ResultSet for a "select * from" query on the metadata table + static class SelectAllFromMetadataTableResultSetMocker + implements org.mockito.stubbing.Answer { - final List rows; + final List rows; int row = -1; - public GetColumnsResultSetMocker(List rows) { + public SelectAllFromMetadataTableResultSetMocker( + List rows) { this.rows = rows; } @@ -4381,14 +4196,14 @@ public Object answer(InvocationOnMock invocation) throws Throwable { if (row >= rows.size()) { return false; } - GetColumnsResultSetMocker.Row currentRow = rows.get(row); + SelectAllFromMetadataTableResultSetMocker.Row currentRow = rows.get(row); ResultSet mock = (ResultSet) invocation.getMock(); - when(mock.getString(JdbcAdmin.METADATA_COL_COLUMN_NAME)).thenReturn(currentRow.columnName); - when(mock.getString(JdbcAdmin.METADATA_COL_DATA_TYPE)).thenReturn(currentRow.dataType); - when(mock.getString(JdbcAdmin.METADATA_COL_KEY_TYPE)).thenReturn(currentRow.keyType); - when(mock.getString(JdbcAdmin.METADATA_COL_CLUSTERING_ORDER)) + when(mock.getString(TableMetadataService.COL_COLUMN_NAME)).thenReturn(currentRow.columnName); + when(mock.getString(TableMetadataService.COL_DATA_TYPE)).thenReturn(currentRow.dataType); + when(mock.getString(TableMetadataService.COL_KEY_TYPE)).thenReturn(currentRow.keyType); + when(mock.getString(TableMetadataService.COL_CLUSTERING_ORDER)) .thenReturn(currentRow.clusteringOrder); - when(mock.getBoolean(JdbcAdmin.METADATA_COL_INDEXED)).thenReturn(currentRow.indexed); + when(mock.getBoolean(TableMetadataService.COL_INDEXED)).thenReturn(currentRow.indexed); return true; } @@ -4415,39 +4230,6 @@ public Row( } } - // Utility class used to mock ResultSet for getTablesNames test - static class GetTablesNamesResultSetMocker implements org.mockito.stubbing.Answer { - - final List rows; - int row = -1; - - public GetTablesNamesResultSetMocker(List rows) { - this.rows = rows; - } - - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - row++; - if (row >= rows.size()) { - return false; - } - GetTablesNamesResultSetMocker.Row currentRow = rows.get(row); - ResultSet mock = (ResultSet) invocation.getMock(); - when(mock.getString(JdbcAdmin.METADATA_COL_FULL_TABLE_NAME)) - .thenReturn(currentRow.fullTableName); - return true; - } - - static class Row { - - final String fullTableName; - - public Row(String fullTableName) { - this.fullTableName = fullTableName; - } - } - } - // Utility class used to mock ResultSet for a "select full_table_name from" query on the metadata // table static class SelectFullTableNameFromMetadataTableResultSetMocker @@ -4469,7 +4251,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } SelectFullTableNameFromMetadataTableResultSetMocker.Row currentRow = rows.get(row); ResultSet mock = (ResultSet) invocation.getMock(); - when(mock.getString(JdbcAdmin.METADATA_COL_FULL_TABLE_NAME)) + when(mock.getString(TableMetadataService.COL_FULL_TABLE_NAME)) .thenReturn(currentRow.fullTableName); return true; } diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithDefaultConfigTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithDefaultConfigTest.java deleted file mode 100644 index 5530b018cd..0000000000 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithDefaultConfigTest.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.scalar.db.storage.jdbc; - -import java.util.Optional; - -public class JdbcAdminWithDefaultConfigTest extends JdbcAdminTestBase { - @Override - Optional getTableMetadataSchemaConfig() { - return Optional.empty(); - } -} diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithTableMetadataSchemaConfigTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithTableMetadataSchemaConfigTest.java deleted file mode 100644 index 27f3f031f1..0000000000 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminWithTableMetadataSchemaConfigTest.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.scalar.db.storage.jdbc; - -import java.util.Optional; - -public class JdbcAdminWithTableMetadataSchemaConfigTest extends JdbcAdminTestBase { - - @Override - Optional getTableMetadataSchemaConfig() { - return Optional.of("my_meta_ns"); - } -} diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcOperationCheckerTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcOperationCheckerTest.java index 716014ddae..e5c18d203c 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcOperationCheckerTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcOperationCheckerTest.java @@ -7,7 +7,6 @@ import static org.mockito.Mockito.when; import com.google.common.collect.Sets; -import com.scalar.db.api.Scan; import com.scalar.db.api.ScanAll; import com.scalar.db.api.Selection; import com.scalar.db.api.Selection.Conjunction; @@ -30,7 +29,6 @@ public class JdbcOperationCheckerTest { @Mock private StorageInfoProvider storageInfoProvider; @Mock private RdbEngineStrategy rdbEngine; @Mock private ScanAll scanAll; - @Mock private Scan scan; @Mock private Selection selection; @Mock private TableMetadata tableMetadata; private JdbcOperationChecker operationChecker; diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineOracleTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineOracleTest.java index 1a58a5e1e3..1bbd0e55db 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineOracleTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEngineOracleTest.java @@ -46,7 +46,8 @@ void createTableInternalSqlsAfterCreateTable_GivenSameClusteringOrders_ShouldNot // Act String[] sqls = - rdbEngine.createTableInternalSqlsAfterCreateTable(false, "myschema", "mytable", metadata); + rdbEngine.createTableInternalSqlsAfterCreateTable( + false, "myschema", "mytable", metadata, false); // Assert assertThat(sqls).hasSize(1); @@ -69,7 +70,8 @@ void createTableInternalSqlsAfterCreateTable_GivenDifferentClusteringOrders_Shou // Act String[] sqls = - rdbEngine.createTableInternalSqlsAfterCreateTable(true, "myschema", "mytable", metadata); + rdbEngine.createTableInternalSqlsAfterCreateTable( + true, "myschema", "mytable", metadata, false); // Assert assertThat(sqls).hasSize(2); diff --git a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEnginePostgresqlTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEnginePostgresqlTest.java index 499d2c91c3..8efd43112a 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/RdbEnginePostgresqlTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/RdbEnginePostgresqlTest.java @@ -25,7 +25,8 @@ void createTableInternalSqlsAfterCreateTable_GivenSameClusteringOrders_ShouldNot // Act String[] sqls = - rdbEngine.createTableInternalSqlsAfterCreateTable(false, "myschema", "mytable", metadata); + rdbEngine.createTableInternalSqlsAfterCreateTable( + false, "myschema", "mytable", metadata, false); // Assert assertThat(sqls).hasSize(0); @@ -47,7 +48,8 @@ void createTableInternalSqlsAfterCreateTable_GivenDifferentClusteringOrders_Shou // Act String[] sqls = - rdbEngine.createTableInternalSqlsAfterCreateTable(true, "myschema", "mytable", metadata); + rdbEngine.createTableInternalSqlsAfterCreateTable( + true, "myschema", "mytable", metadata, false); // Assert assertThat(sqls).hasSize(1);