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 4afdb05a7..578eed34a 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 @@ -30,25 +30,26 @@ public JdbcAdminTestUtils(Properties properties) { @Override public void dropNamespacesTable() throws Exception { execute( - "DROP TABLE " + rdbEngine.encloseFullTableName(metadataSchema, JdbcAdmin.NAMESPACES_TABLE)); + "DROP TABLE " + + rdbEngine.encloseFullTableName(metadataSchema, NamespaceMetadataService.TABLE_NAME)); } @Override public void dropMetadataTable() throws Exception { - dropTable(metadataSchema, JdbcAdmin.METADATA_TABLE); + dropTable(metadataSchema, TableMetadataService.TABLE_NAME); } @Override public void truncateNamespacesTable() throws Exception { String truncateTableStatement = - rdbEngine.truncateTableSql(metadataSchema, JdbcAdmin.NAMESPACES_TABLE); + rdbEngine.truncateTableSql(metadataSchema, NamespaceMetadataService.TABLE_NAME); execute(truncateTableStatement); } @Override public void truncateMetadataTable() throws Exception { String truncateTableStatement = - rdbEngine.truncateTableSql(metadataSchema, JdbcAdmin.METADATA_TABLE); + rdbEngine.truncateTableSql(metadataSchema, TableMetadataService.TABLE_NAME); execute(truncateTableStatement); } @@ -57,7 +58,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')"; @@ -68,9 +69,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 e1b1c9cac..8ef82c502 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 @@ -9,12 +9,13 @@ import com.scalar.db.storage.cassandra.CassandraAdmin; import com.scalar.db.storage.cassandra.CassandraConfig; 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.NamespaceMetadataService; import com.scalar.db.storage.jdbc.RdbEngineFactory; 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; @@ -61,7 +62,8 @@ public void dropNamespacesTable() throws SQLException { // for JDBC execute( "DROP TABLE " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.NAMESPACES_TABLE)); + + rdbEngine.encloseFullTableName( + jdbcMetadataSchema, NamespaceMetadataService.TABLE_NAME)); } @Override @@ -71,7 +73,7 @@ public void dropMetadataTable() throws SQLException { // for JDBC execute( "DROP TABLE " - + rdbEngine.encloseFullTableName(jdbcMetadataSchema, JdbcAdmin.METADATA_TABLE)); + + rdbEngine.encloseFullTableName(jdbcMetadataSchema, TableMetadataService.TABLE_NAME)); } @Override @@ -86,7 +88,7 @@ public void truncateNamespacesTable() throws SQLException { // for JDBC String truncateTableStatement = - rdbEngine.truncateTableSql(jdbcMetadataSchema, JdbcAdmin.NAMESPACES_TABLE); + rdbEngine.truncateTableSql(jdbcMetadataSchema, NamespaceMetadataService.TABLE_NAME); execute(truncateTableStatement); } @@ -97,7 +99,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); } @@ -109,7 +111,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')"; @@ -123,9 +125,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/storage/jdbc/JdbcAdmin.java b/core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java index 8b78637d4..d9d729070 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; @@ -40,25 +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"; - public static final String NAMESPACES_TABLE = "namespaces"; - @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"; - @VisibleForTesting static final String NAMESPACE_COL_NAMESPACE_NAME = "namespace_name"; - 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( @@ -69,21 +59,24 @@ public class JdbcAdmin implements DistributedStorageAdmin { private final RdbEngineStrategy rdbEngine; private final BasicDataSource dataSource; - private final String metadataSchema; + private final TableMetadataService tableMetadataService; + private final NamespaceMetadataService namespaceMetadataService; @Inject public JdbcAdmin(DatabaseConfig databaseConfig) { JdbcConfig config = new JdbcConfig(databaseConfig); rdbEngine = RdbEngineFactory.create(config); dataSource = JdbcUtils.initDataSourceForAdmin(config, rdbEngine); - metadataSchema = config.getMetadataSchema(); + tableMetadataService = new TableMetadataService(config.getMetadataSchema(), rdbEngine); + namespaceMetadataService = new NamespaceMetadataService(config.getMetadataSchema(), rdbEngine); } @SuppressFBWarnings("EI_EXPOSE_REP2") public JdbcAdmin(BasicDataSource dataSource, JdbcConfig config) { rdbEngine = RdbEngineFactory.create(config); this.dataSource = dataSource; - metadataSchema = config.getMetadataSchema(); + tableMetadataService = new TableMetadataService(config.getMetadataSchema(), rdbEngine); + namespaceMetadataService = new NamespaceMetadataService(config.getMetadataSchema(), rdbEngine); } private static boolean hasDescClusteringOrder(TableMetadata metadata) { @@ -105,45 +98,6 @@ static boolean hasDifferentClusteringOrders(TableMetadata metadata) { return hasAscOrder && hasDescOrder; } - 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); - } - } - - 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(); - } - } - - static void execute(Connection connection, String[] sqls) throws SQLException { - execute(connection, sqls, null); - } - - static void execute( - Connection connection, String[] sqls, @Nullable SqlWarningHandler warningHandler) - throws SQLException { - for (String sql : sqls) { - execute(connection, sql, warningHandler); - } - } - @Override public void createNamespace(String namespace, Map options) throws ExecutionException { @@ -152,7 +106,7 @@ public void createNamespace(String namespace, Map options) try (Connection connection = dataSource.getConnection()) { execute(connection, rdbEngine.createSchemaSqls(namespace)); createNamespacesTableIfNotExists(connection); - insertIntoNamespacesTable(connection, namespace); + namespaceMetadataService.insertIntoNamespacesTable(connection, namespace); } catch (SQLException e) { throw new ExecutionException("Creating the " + namespace + " schema failed", e); } @@ -163,7 +117,6 @@ public void createTable( String namespace, String table, TableMetadata metadata, Map options) throws ExecutionException { try (Connection connection = dataSource.getConnection()) { - createNamespacesTableIfNotExists(connection); createTableInternal(connection, namespace, table, metadata, false); addTableMetadata(connection, namespace, table, metadata, true, false); } catch (SQLException e) { @@ -208,117 +161,6 @@ void createTableInternal( createIndex(connection, schema, table, metadata, ifNotExists); } - private void createIndex( - Connection connection, - String schema, - String table, - TableMetadata metadata, - boolean ifNotExists) - throws SQLException { - for (String indexedColumn : metadata.getSecondaryIndexNames()) { - createIndex(connection, schema, table, indexedColumn, ifNotExists); - } - } - - @VisibleForTesting - void addTableMetadata( - Connection connection, - String namespace, - String table, - TableMetadata metadata, - boolean createMetadataTable, - boolean overwriteMetadata) - throws SQLException { - if (createMetadataTable) { - createMetadataSchemaAndTableIfNotExists(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); - } - } - - private void createMetadataSchemaAndTableIfNotExists(Connection connection) throws SQLException { - createSchemaIfNotExists(connection, metadataSchema); - createMetadataTableIfNotExists(connection); - } - - 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; - } - } - } - - @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) - + "))"; - - createTable(connection, createTableStatement, true); - } - - 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 createTableInternalSqlsAfterCreateTable( Connection connection, String schema, @@ -338,75 +180,23 @@ private void createTableInternalSqlsAfterCreateTable( } } - 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); - deleteNamespacesTableAndMetadataSchemaIfEmpty(connection); + tableMetadataService.deleteTableMetadata(connection, namespace, table, true); } catch (SQLException e) { throw new ExecutionException( "Dropping the " + getFullTableName(namespace, table) + " table failed", e); @@ -419,61 +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) { - 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, METADATA_TABLE) - + " WHERE " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " = '" - + getFullTableName(schema, table) - + "'"; - } - - private void deleteMetadataTableIfEmpty(Connection connection) throws SQLException { - if (isMetadataTableEmpty(connection)) { - deleteTable(connection, encloseFullTableName(metadataSchema, METADATA_TABLE)); - } - } - - 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 deleteTable(Connection connection, String fullTableName) throws SQLException { - String dropTableStatement = "DROP TABLE " + fullTableName; - - 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()) { @@ -484,13 +219,31 @@ public void dropNamespace(String namespace) throws ExecutionException { namespace, remainingTables)); } execute(connection, rdbEngine.dropNamespaceSql(namespace)); - deleteFromNamespacesTable(connection, namespace); - deleteNamespacesTableAndMetadataSchemaIfEmpty(connection); + namespaceMetadataService.deleteFromNamespacesTable(connection, namespace); + namespaceMetadataService.deleteNamespacesTableIfEmpty(connection); } catch (SQLException e) { rdbEngine.dropNamespaceTranslateSQLException(e, namespace); } } + 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); @@ -504,67 +257,16 @@ public void truncateTable(String namespace, String table) throws ExecutionExcept @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); - - 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) { - // An exception will be thrown if the namespace table does not exist when executing the select - // query - if (rdbEngine.isUndefinedTableError(e)) { - return null; - } throw new ExecutionException( "Getting a table metadata for the " + getFullTableName(namespace, table) + " table failed", e); } - - if (!tableExists) { - return null; - } - - return builder.build(); } @VisibleForTesting @@ -644,105 +346,23 @@ public void importTable( } } - 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( "Getting the list of tables of the " + namespace + " schema 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)); - } - } - } - return tableNames; - } - @Override public boolean namespaceExists(String namespace) throws ExecutionException { - String selectQuery = - "SELECT 1 FROM " - + encloseFullTableName(metadataSchema, NAMESPACES_TABLE) - + " WHERE " - + enclose(NAMESPACE_COL_NAMESPACE_NAME) - + " = ?"; try (Connection connection = dataSource.getConnection()) { rdbEngine.setConnectionToReadOnly(connection, true); - - try (PreparedStatement statement = connection.prepareStatement(selectQuery)) { - statement.setString(1, namespace); - try (ResultSet resultSet = statement.executeQuery()) { - return resultSet.next(); - } - } + return namespaceMetadataService.namespaceExists(connection, namespace); } catch (SQLException e) { - // An exception will be thrown if the namespaces table does not exist when executing the - // select query - if (rdbEngine.isUndefinedTableError(e)) { - return false; - } throw new ExecutionException("Checking if the " + namespace + " schema exists failed", e); } } @@ -786,7 +406,7 @@ public void createIndex( try (Connection connection = dataSource.getConnection()) { alterToIndexColumnTypeIfNecessary(connection, namespace, table, columnName); createIndex(connection, namespace, table, columnName, false); - updateTableMetadata(connection, namespace, table, columnName, true); + tableMetadataService.updateTableMetadata(connection, namespace, table, columnName, true); } catch (ExecutionException | SQLException e) { throw new ExecutionException( String.format( @@ -836,7 +456,7 @@ 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( String.format( @@ -846,26 +466,6 @@ columnName, getFullTableName(namespace, table)), } } - 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); - } - } - @Override public void repairNamespace(String namespace, Map options) throws ExecutionException { @@ -884,8 +484,6 @@ public void repairNamespace(String namespace, Map options) public void repairTable( String namespace, String table, TableMetadata metadata, Map options) throws ExecutionException { - rdbEngine.throwIfInvalidNamespaceName(table); - try (Connection connection = dataSource.getConnection()) { createTableInternal(connection, namespace, table, metadata, true); addTableMetadata(connection, namespace, table, metadata, true, true); @@ -1029,13 +627,15 @@ 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); @@ -1053,6 +653,20 @@ public void renameTable(String namespace, String oldTableName, String newTableNa } } + 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); + } + } + @VisibleForTesting void createIndex( Connection connection, String schema, String table, String indexedColumn, boolean ifNotExists) @@ -1082,230 +696,165 @@ private void dropIndex(Connection connection, String schema, String table, Strin execute(connection, sql); } - 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 String getIndexName(String schema, String table, String indexedColumn) { return String.join("_", INDEX_NAME_PREFIX, schema, table, indexedColumn); } - 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 enclose(String name) { - return rdbEngine.enclose(name); - } - - private String encloseFullTableName(String schema, String table) { - return rdbEngine.encloseFullTableName(schema, table); - } - @Override public Set getNamespaceNames() throws ExecutionException { try (Connection connection = dataSource.getConnection()) { rdbEngine.setConnectionToReadOnly(connection, true); - - String selectQuery = - "SELECT * FROM " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE); - Set namespaces = new HashSet<>(); - try (PreparedStatement preparedStatement = connection.prepareStatement(selectQuery); - ResultSet resultSet = preparedStatement.executeQuery()) { - while (resultSet.next()) { - namespaces.add(resultSet.getString(NAMESPACE_COL_NAMESPACE_NAME)); - } - return namespaces; - } + return namespaceMetadataService.getNamespaceNames(connection); } 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.emptySet(); - } throw new ExecutionException("Getting the existing schema names failed", e); } } - @VisibleForTesting - void createNamespacesTableIfNotExists(Connection connection) throws ExecutionException { - if (tableExistsInternal(connection, metadataSchema, NAMESPACES_TABLE)) { - return; - } + @Override + public void upgrade(Map options) throws ExecutionException { + try (Connection connection = dataSource.getConnection()) { + Set namespaceNamesOfExistingTables = + tableMetadataService.getNamespaceNamesOfExistingTables(connection); + if (namespaceNamesOfExistingTables.isEmpty()) { + // No existing tables, so no need to upgrade + return; + } - try { - createSchemaIfNotExists(connection, metadataSchema); - String createTableStatement = - "CREATE TABLE " - + encloseFullTableName(metadataSchema, NAMESPACES_TABLE) - + "(" - + enclose(NAMESPACE_COL_NAMESPACE_NAME) - + " " - + getTextType(128, true) - + ", " - + "PRIMARY KEY (" - + enclose(NAMESPACE_COL_NAMESPACE_NAME) - + "))"; - createTable(connection, createTableStatement, true); - - // Insert the system namespace to the namespaces table - insertIntoNamespacesTable(connection, metadataSchema); + createNamespacesTableIfNotExists(connection); + for (String namespace : namespaceNamesOfExistingTables) { + upsertIntoNamespacesTable(connection, namespace); + } } catch (SQLException e) { - throw new ExecutionException("Creating the namespace table failed", e); + throw new ExecutionException("Upgrading the ScalarDB environment failed", e); } } - private void insertIntoNamespacesTable(Connection connection, String namespaceName) + @Override + public StorageInfo getStorageInfo(String namespace) { + return STORAGE_INFO; + } + + @VisibleForTesting + void addTableMetadata( + Connection connection, + String namespace, + String table, + TableMetadata metadata, + boolean createMetadataTable, + boolean overwriteMetadata) throws SQLException { - String insertStatement = - "INSERT INTO " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE) + " VALUES (?)"; - try (PreparedStatement preparedStatement = connection.prepareStatement(insertStatement)) { - preparedStatement.setString(1, namespaceName); - preparedStatement.execute(); - } + tableMetadataService.addTableMetadata( + connection, namespace, table, metadata, createMetadataTable, overwriteMetadata); } @VisibleForTesting - void upsertIntoNamespacesTable(Connection connection, String namespace) throws SQLException { - try { - insertIntoNamespacesTable(connection, namespace); - } catch (SQLException e) { - // ignore if the schema already exists - if (!rdbEngine.isDuplicateKeyError(e)) { - throw e; - } - } + void createTableMetadataTableIfNotExists(Connection connection) throws SQLException { + tableMetadataService.createTableMetadataTableIfNotExists(connection); } - private void deleteFromNamespacesTable(Connection connection, String namespaceName) - throws SQLException { - String deleteStatement = - "DELETE FROM " - + encloseFullTableName(metadataSchema, NAMESPACES_TABLE) - + " WHERE " - + enclose(NAMESPACE_COL_NAMESPACE_NAME) - + " = ?"; - try (PreparedStatement preparedStatement = connection.prepareStatement(deleteStatement)) { - preparedStatement.setString(1, namespaceName); - preparedStatement.execute(); - } + @VisibleForTesting + void createNamespacesTableIfNotExists(Connection connection) throws SQLException { + namespaceMetadataService.createNamespacesTableIfNotExists(connection); } - private void deleteNamespacesTableAndMetadataSchemaIfEmpty(Connection connection) - throws SQLException { - if (areNamespacesTableAndMetadataSchemaEmpty(connection)) { - deleteTable(connection, encloseFullTableName(metadataSchema, NAMESPACES_TABLE)); - deleteMetadataSchema(connection); - } + @VisibleForTesting + void upsertIntoNamespacesTable(Connection connection, String namespace) throws SQLException { + namespaceMetadataService.upsertIntoNamespacesTable(connection, namespace); } - private boolean areNamespacesTableAndMetadataSchemaEmpty(Connection connection) + private void createTable(Connection connection, String createTableStatement, boolean ifNotExists) throws SQLException { - String selectAllTables = - "SELECT * FROM " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE); - - Set namespaces = new HashSet<>(); - try (Statement statement = connection.createStatement(); - ResultSet results = statement.executeQuery(selectAllTables)) { - int count = 0; - while (results.next()) { - namespaces.add(results.getString(NAMESPACE_COL_NAMESPACE_NAME)); - // Only need to fetch the first two rows - if (count++ == 2) { - break; - } - } + String stmt = createTableStatement; + if (ifNotExists) { + stmt = rdbEngine.tryAddIfNotExistsToCreateTableSql(createTableStatement); } - - boolean onlyMetadataNamespaceLeft = - namespaces.size() == 1 && namespaces.contains(metadataSchema); - if (!onlyMetadataNamespaceLeft) { - return false; + try { + execute(connection, stmt); + } catch (SQLException e) { + // Suppress the exception thrown when the table already exists + if (!(ifNotExists && rdbEngine.isDuplicateTableError(e))) { + throw e; + } } + } - // Check if the metadata table exists. If it does not, the metadata schema is empty. - String sql = - rdbEngine.tableExistsInternalTableCheckSql( - encloseFullTableName(metadataSchema, METADATA_TABLE)); + 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 false; + 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 true; + return false; } - throw e; + throw new ExecutionException( + String.format( + "Checking if the %s table exists failed", getFullTableName(namespace, table)), + e); } } - @Override - public void upgrade(Map options) throws ExecutionException { - try (Connection connection = dataSource.getConnection()) { - if (tableExistsInternal(connection, metadataSchema, METADATA_TABLE)) { - createNamespacesTableIfNotExists(connection); - importNamespaceNamesOfExistingTables(connection); - } + private void createSchemaIfNotExists(Connection connection, String schema) throws SQLException { + String[] sqls = rdbEngine.createSchemaIfNotExistsSqls(schema); + try { + execute(connection, sqls); } catch (SQLException e) { - throw new ExecutionException("Upgrading the ScalarDB environment failed", e); + // Suppress exceptions indicating the duplicate metadata schema + if (!rdbEngine.isCreateMetadataSchemaDuplicateSchemaError(e)) { + throw e; + } } } - private void importNamespaceNamesOfExistingTables(Connection connection) - throws ExecutionException { - String selectAllTableNames = - "SELECT DISTINCT " - + enclose(METADATA_COL_FULL_TABLE_NAME) - + " FROM " - + encloseFullTableName(metadataSchema, METADATA_TABLE); - 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); - } - for (String namespace : namespaceOfExistingTables) { - upsertIntoNamespacesTable(connection, namespace); - } - } catch (SQLException e) { - throw new ExecutionException("Importing the namespace names of existing tables failed", e); + private String enclose(String name) { + return rdbEngine.enclose(name); + } + + 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); } } - @Override - public StorageInfo getStorageInfo(String namespace) { - return STORAGE_INFO; + 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(); + } + } + + static void execute(Connection connection, String[] sqls) throws SQLException { + execute(connection, sqls, null); + } + + static void execute( + Connection connection, String[] sqls, @Nullable SqlWarningHandler warningHandler) + throws SQLException { + for (String sql : sqls) { + execute(connection, sql, warningHandler); + } } @FunctionalInterface diff --git a/core/src/main/java/com/scalar/db/storage/jdbc/NamespaceMetadataService.java b/core/src/main/java/com/scalar/db/storage/jdbc/NamespaceMetadataService.java new file mode 100644 index 000000000..25ff95223 --- /dev/null +++ b/core/src/main/java/com/scalar/db/storage/jdbc/NamespaceMetadataService.java @@ -0,0 +1,223 @@ +package com.scalar.db.storage.jdbc; + +import static com.scalar.db.storage.jdbc.JdbcAdmin.execute; + +import com.google.common.annotations.VisibleForTesting; +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.Set; + +@SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION") +public class NamespaceMetadataService { + @VisibleForTesting public static final String TABLE_NAME = "namespaces"; + @VisibleForTesting static final String COL_NAMESPACE_NAME = "namespace_name"; + + private final String metadataSchema; + private final RdbEngineStrategy rdbEngine; + + NamespaceMetadataService(String metadataSchema, RdbEngineStrategy rdbEngine) { + this.metadataSchema = metadataSchema; + this.rdbEngine = rdbEngine; + } + + void createNamespacesTableIfNotExists(Connection connection) throws SQLException { + if (tableExistsInternal(connection, metadataSchema, TABLE_NAME)) { + return; + } + + createSchemaIfNotExists(connection, metadataSchema); + String createTableStatement = + "CREATE TABLE " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + "(" + + enclose(COL_NAMESPACE_NAME) + + " " + + getTextType(128, true) + + ", " + + "PRIMARY KEY (" + + enclose(COL_NAMESPACE_NAME) + + "))"; + createTable(connection, createTableStatement, true); + + // Insert the system namespace to the namespaces table + insertIntoNamespacesTable(connection, metadataSchema); + } + + void deleteNamespacesTableIfEmpty(Connection connection) throws SQLException { + if (isNamespacesTableEmpty(connection)) { + deleteTable(connection, encloseFullTableName(metadataSchema, TABLE_NAME)); + deleteMetadataSchema(connection); + } + } + + private boolean isNamespacesTableEmpty(Connection connection) throws SQLException { + String selectAllTables = "SELECT * FROM " + encloseFullTableName(metadataSchema, TABLE_NAME); + + Set namespaces = new HashSet<>(); + try (Statement statement = connection.createStatement(); + ResultSet results = statement.executeQuery(selectAllTables)) { + int count = 0; + while (results.next()) { + namespaces.add(results.getString(COL_NAMESPACE_NAME)); + // Only need to fetch the first two rows + if (count++ == 2) { + break; + } + } + } + + return namespaces.size() == 1 && namespaces.contains(metadataSchema); + } + + private void deleteMetadataSchema(Connection connection) throws SQLException { + String sql = rdbEngine.deleteMetadataSchemaSql(metadataSchema); + execute(connection, sql); + } + + void insertIntoNamespacesTable(Connection connection, String namespaceName) throws SQLException { + String insertStatement = + "INSERT INTO " + encloseFullTableName(metadataSchema, TABLE_NAME) + " VALUES (?)"; + try (PreparedStatement preparedStatement = connection.prepareStatement(insertStatement)) { + preparedStatement.setString(1, namespaceName); + preparedStatement.execute(); + } + } + + void upsertIntoNamespacesTable(Connection connection, String namespace) throws SQLException { + try { + insertIntoNamespacesTable(connection, namespace); + } catch (SQLException e) { + // ignore if the schema already exists + if (!rdbEngine.isDuplicateKeyError(e)) { + throw e; + } + } + } + + void deleteFromNamespacesTable(Connection connection, String namespaceName) throws SQLException { + String deleteStatement = + "DELETE FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " WHERE " + + enclose(COL_NAMESPACE_NAME) + + " = ?"; + try (PreparedStatement preparedStatement = connection.prepareStatement(deleteStatement)) { + preparedStatement.setString(1, namespaceName); + preparedStatement.execute(); + } + } + + boolean namespaceExists(Connection connection, String namespace) throws SQLException { + String selectQuery = + "SELECT 1 FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME) + + " WHERE " + + enclose(COL_NAMESPACE_NAME) + + " = ?"; + try { + try (PreparedStatement statement = connection.prepareStatement(selectQuery)) { + statement.setString(1, namespace); + try (ResultSet resultSet = statement.executeQuery()) { + return resultSet.next(); + } + } + } catch (SQLException e) { + // An exception will be thrown if the namespaces table does not exist when executing the + // select query + if (rdbEngine.isUndefinedTableError(e)) { + return false; + } + throw e; + } + } + + Set getNamespaceNames(Connection connection) throws SQLException { + try { + String selectQuery = "SELECT * FROM " + encloseFullTableName(metadataSchema, TABLE_NAME); + Set namespaces = new HashSet<>(); + try (PreparedStatement preparedStatement = connection.prepareStatement(selectQuery); + ResultSet resultSet = preparedStatement.executeQuery()) { + while (resultSet.next()) { + namespaces.add(resultSet.getString(COL_NAMESPACE_NAME)); + } + return namespaces; + } + } 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.emptySet(); + } + throw e; + } + } + + 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 boolean tableExistsInternal(Connection connection, String namespace, String table) + throws SQLException { + 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 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/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 000000000..ba8c32643 --- /dev/null +++ b/core/src/main/java/com/scalar/db/storage/jdbc/TableMetadataService.java @@ -0,0 +1,416 @@ +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 { + 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 getNamespaceNamesOfExistingTables(Connection connection) throws SQLException { + if (!tableExistsInternal(connection, metadataSchema, TABLE_NAME)) { + return Collections.emptySet(); + } + + String selectAllTableNames = + "SELECT DISTINCT " + + enclose(COL_FULL_TABLE_NAME) + + " FROM " + + encloseFullTableName(metadataSchema, TABLE_NAME); + 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); + } + return namespaceOfExistingTables; + } + } + + 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 boolean tableExistsInternal(Connection connection, String namespace, String table) + throws SQLException { + 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 e; + } + } + + private void deleteTable(Connection connection, String fullTableName) throws SQLException { + String dropTableStatement = "DROP TABLE " + fullTableName; + + execute(connection, dropTableStatement); + } + + 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/JdbcAdminTest.java b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java index c17d2b02c..7e2d4d080 100644 --- a/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java +++ b/core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java @@ -76,7 +76,6 @@ 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, @@ -501,6 +500,19 @@ private void createNamespace_forX_shouldExecuteCreateNamespaceStatement( verify(mockedInsertNamespaceStatement2).execute(); } + @Test + 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 @@ -915,7 +927,6 @@ public void addTableMetadata_ifNotExistsAndOverwriteMetadataForMysql_ShouldWorkP throws Exception { addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.MYSQL, - "CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`", "CREATE TABLE IF NOT EXISTS `" + METADATA_SCHEMA + "`.`metadata`(" @@ -943,7 +954,6 @@ public void addTableMetadata_ifNotExistsAndOverwriteMetadataForPostgresql_Should throws Exception { addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.POSTGRESQL, - "CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\"", "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"metadata\"(" @@ -971,7 +981,6 @@ public void addTableMetadata_ifNotExistsAndOverwriteMetadataForSqlServer_ShouldW throws Exception { addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.SQL_SERVER, - "CREATE SCHEMA [" + METADATA_SCHEMA + "]", "CREATE TABLE [" + METADATA_SCHEMA + "].[metadata](" @@ -999,8 +1008,6 @@ public void addTableMetadata_ifNotExistsAndOverwriteMetadataForOracle_ShouldWork throws Exception { addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.ORACLE, - "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\"))", @@ -1047,7 +1054,6 @@ public void addTableMetadata_ifNotExistsAndOverwriteMetadataForDb2_ShouldWorkPro throws Exception { addTableMetadata_createMetadataTableIfNotExistsForXAndOverwriteMetadata_ShouldWorkProperly( RdbEngine.DB2, - "CREATE SCHEMA \"" + METADATA_SCHEMA + "\"", "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"metadata\"(" @@ -1109,7 +1115,6 @@ public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForMysql_Should throws Exception { addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.MYSQL, - "CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`", "CREATE TABLE IF NOT EXISTS `" + METADATA_SCHEMA + "`.`metadata`(" @@ -1154,7 +1159,6 @@ public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForMysql_Should throws Exception { addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.POSTGRESQL, - "CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\"", "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"metadata\"(" @@ -1198,7 +1202,6 @@ public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForSqlServer_Sh throws Exception { addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.SQL_SERVER, - "CREATE SCHEMA [" + METADATA_SCHEMA + "]", "CREATE TABLE [" + METADATA_SCHEMA + "].[metadata](" @@ -1242,8 +1245,6 @@ public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForOracle_Shoul throws Exception { addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.ORACLE, - "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\"))", @@ -1322,7 +1323,6 @@ public void addTableMetadata_ifNotExistsAndDoNotOverwriteMetadataForDb2_ShouldWo throws Exception { addTableMetadata_createMetadataTableIfNotExistsForX_ShouldWorkProperly( RdbEngine.DB2, - "CREATE SCHEMA \"" + METADATA_SCHEMA + "\"", "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"metadata\"(" @@ -1519,7 +1519,7 @@ public void repairTable_WhenTableAlreadyExistsWithoutIndex_ShouldCreateIndex(Rdb public void createMetadataTableIfNotExists_WithInternalDbError_forMysql_shouldThrowInternalDbError() throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( RdbEngine.MYSQL, new CommunicationsException("", null)); } @@ -1527,7 +1527,7 @@ public void repairTable_WhenTableAlreadyExistsWithoutIndex_ShouldCreateIndex(Rdb public void createMetadataTableIfNotExists_WithInternalDbError_forPostgresql_shouldThrowInternalDbError() throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( RdbEngine.POSTGRESQL, new PSQLException("", PSQLState.CONNECTION_FAILURE)); } @@ -1535,19 +1535,20 @@ public void repairTable_WhenTableAlreadyExistsWithoutIndex_ShouldCreateIndex(Rdb public void createMetadataTableIfNotExists_WithInternalDbError_forSqlite_shouldThrowInternalDbError() throws SQLException { - createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( + createTableMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( RdbEngine.SQLITE, new SQLiteException("", SQLiteErrorCode.SQLITE_IOERR)); } - private void createMetadataTableIfNotExists_WithInternalDbError_forX_shouldThrowInternalDbError( - RdbEngine rdbEngine, SQLException internalDbError) throws SQLException { + 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.createMetadataTableIfNotExists(connection)) + assertThatThrownBy(() -> admin.createTableMetadataTableIfNotExists(connection)) .isInstanceOf(internalDbError.getClass()); } @@ -1622,8 +1623,7 @@ public void dropTable_forMysqlWithNoMoreMetadataAfterDeletion_shouldDropTableAnd + 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`", - "SELECT * FROM `" + METADATA_SCHEMA + "`.`namespaces`"); + "DROP TABLE `" + METADATA_SCHEMA + "`.`metadata`"); } @Test @@ -1637,8 +1637,7 @@ public void dropTable_forMysqlWithNoMoreMetadataAfterDeletion_shouldDropTableAnd + 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\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); } @Test @@ -1652,8 +1651,7 @@ public void dropTable_forMysqlWithNoMoreMetadataAfterDeletion_shouldDropTableAnd + 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]", - "SELECT * FROM [" + METADATA_SCHEMA + "].[namespaces]"); + "DROP TABLE [" + METADATA_SCHEMA + "].[metadata]"); } @Test @@ -1666,8 +1664,7 @@ public void dropTable_forOracleWithNoMoreMetadataAfterDeletion_shouldDropTableAn + 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\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); } @Test @@ -1680,8 +1677,7 @@ public void dropTable_forSqliteWithNoMoreMetadataAfterDeletion_shouldDropTableAn + 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\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "$namespaces\""); + "DROP TABLE \"" + METADATA_SCHEMA + "$metadata\""); } @Test @@ -1694,8 +1690,7 @@ public void dropTable_forDb2WithNoMoreMetadataAfterDeletion_shouldDropTableAndDe + 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\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "DROP TABLE \"" + METADATA_SCHEMA + "\".\"metadata\""); } private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDeleteMetadata( @@ -1754,8 +1749,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM `" + METADATA_SCHEMA + "`.`metadata` WHERE `full_table_name` = 'my_ns.foo_table'", - "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`", - "SELECT * FROM `" + METADATA_SCHEMA + "`.`namespaces`"); + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`"); } @Test @@ -1768,8 +1762,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM \"" + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); } @Test @@ -1782,8 +1775,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM [" + METADATA_SCHEMA + "].[metadata] WHERE [full_table_name] = 'my_ns.foo_table'", - "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]", - "SELECT * FROM [" + METADATA_SCHEMA + "].[namespaces]"); + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]"); } @Test @@ -1796,8 +1788,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM \"" + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); } @Test @@ -1810,8 +1801,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM \"" + METADATA_SCHEMA + "$metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "$namespaces\""); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\""); } @Test @@ -1824,8 +1814,7 @@ private void dropTable_forXWithNoMoreMetadataAfterDeletion_shouldDropTableAndDel "DELETE FROM \"" + METADATA_SCHEMA + "\".\"metadata\" WHERE \"full_table_name\" = 'my_ns.foo_table'", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", - "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\""); + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\""); } private void @@ -1883,7 +1872,6 @@ public void dropNamespace_WithOnlyNamespaceSchemaLeftForMysql_shouldDropSchemaAn "DROP SCHEMA `my_ns`", "DELETE FROM `" + METADATA_SCHEMA + "`.`namespaces` WHERE `namespace_name` = ?", "SELECT * FROM `" + METADATA_SCHEMA + "`.`namespaces`", - "SELECT 1 FROM `" + METADATA_SCHEMA + "`.`metadata` LIMIT 1", "DROP TABLE `" + METADATA_SCHEMA + "`.`namespaces`", "DROP SCHEMA `" + METADATA_SCHEMA + "`"); } @@ -1897,7 +1885,6 @@ public void dropNamespace_WithOnlyNamespaceSchemaLeftForMysql_shouldDropSchemaAn "DROP SCHEMA \"my_ns\"", "DELETE FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" WHERE \"namespace_name\" = ?", "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\"", - "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" LIMIT 1", "DROP TABLE \"" + METADATA_SCHEMA + "\".\"namespaces\"", "DROP SCHEMA \"" + METADATA_SCHEMA + "\""); } @@ -1911,7 +1898,6 @@ public void dropNamespace_WithOnlyNamespaceSchemaLeftForMysql_shouldDropSchemaAn "DROP SCHEMA [my_ns]", "DELETE FROM [" + METADATA_SCHEMA + "].[namespaces] WHERE [namespace_name] = ?", "SELECT * FROM [" + METADATA_SCHEMA + "].[namespaces]", - "SELECT TOP 1 1 FROM [" + METADATA_SCHEMA + "].[metadata]", "DROP TABLE [" + METADATA_SCHEMA + "].[namespaces]", "DROP SCHEMA [" + METADATA_SCHEMA + "]"); } @@ -1925,7 +1911,6 @@ public void dropNamespace_WithOnlyNamespaceSchemaLeftForMysql_shouldDropSchemaAn "DROP USER \"my_ns\"", "DELETE FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" WHERE \"namespace_name\" = ?", "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\"", - "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" FETCH FIRST 1 ROWS ONLY", "DROP TABLE \"" + METADATA_SCHEMA + "\".\"namespaces\"", "DROP USER \"" + METADATA_SCHEMA + "\""); } @@ -1938,7 +1923,6 @@ public void dropNamespace_WithOnlyNamespaceSchemaLeftForDb2_shouldDropSchemaAndN "DROP SCHEMA \"my_ns\" RESTRICT", "DELETE FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" WHERE \"namespace_name\" = ?", "SELECT * FROM \"" + METADATA_SCHEMA + "\".\"namespaces\"", - "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" LIMIT 1", "DROP TABLE \"" + METADATA_SCHEMA + "\".\"namespaces\"", "DROP SCHEMA \"" + METADATA_SCHEMA + "\" RESTRICT"); } @@ -1950,8 +1934,6 @@ public void dropNamespace_forSqlite_shouldDropNamespace() throws Exception { "DELETE FROM \"" + METADATA_SCHEMA + "$namespaces\" WHERE \"namespace_name\" = ?"; String selectAllFromNamespaceTableQuery = "SELECT * FROM \"" + METADATA_SCHEMA + "$namespaces\""; - String selectAllFromMetadataTableQuery = - "SELECT 1 FROM \"" + METADATA_SCHEMA + "$metadata\" LIMIT 1"; String dropNamespaceTableQuery = "DROP TABLE \"" + METADATA_SCHEMA + "$namespaces\""; String namespace = "my_ns"; @@ -1960,14 +1942,10 @@ public void dropNamespace_forSqlite_shouldDropNamespace() throws Exception { Connection connection = mock(Connection.class); PreparedStatement deleteFromNamespaceTablePrepStmt = mock(PreparedStatement.class); Statement selectAllFromNamespaceTablePrepStmt = mock(Statement.class); - Statement selectAllFromMetadataTablePrepStmt = mock(Statement.class); Statement dropNamespaceTableStmt = mock(Statement.class); when(dataSource.getConnection()).thenReturn(connection); when(connection.createStatement()) - .thenReturn( - selectAllFromNamespaceTablePrepStmt, - selectAllFromMetadataTablePrepStmt, - dropNamespaceTableStmt); + .thenReturn(selectAllFromNamespaceTablePrepStmt, dropNamespaceTableStmt); when(connection.prepareStatement(anyString())).thenReturn(deleteFromNamespaceTablePrepStmt); when(dataSource.getConnection()).thenReturn(connection); // Only the metadata schema is left @@ -1976,10 +1954,6 @@ public void dropNamespace_forSqlite_shouldDropNamespace() throws Exception { new SelectNamespaceNameFromNamespaceTableResultSetMocker.Row(METADATA_SCHEMA)); when(selectAllFromNamespaceTablePrepStmt.executeQuery(anyString())).thenReturn(resultSet1); - SQLException sqlException = mock(SQLException.class); - mockUndefinedTableError(RdbEngine.SQLITE, sqlException); - when(selectAllFromMetadataTablePrepStmt.execute(anyString())).thenThrow(sqlException); - // Act admin.dropNamespace(namespace); @@ -1987,7 +1961,6 @@ public void dropNamespace_forSqlite_shouldDropNamespace() throws Exception { verify(deleteFromNamespaceTablePrepStmt).setString(1, namespace); verify(connection).prepareStatement(deleteFromNamespaceTableQuery); verify(selectAllFromNamespaceTablePrepStmt).executeQuery(selectAllFromNamespaceTableQuery); - verify(selectAllFromMetadataTablePrepStmt).execute(selectAllFromMetadataTableQuery); verify(dropNamespaceTableStmt).execute(dropNamespaceTableQuery); } @@ -1996,7 +1969,6 @@ private void dropNamespace_WithOnlyNamespaceSchemaLeftForX_shouldDropSchemaAndNa String dropNamespaceQuery, String deleteFromNamespaceTableQuery, String selectAllFromNamespaceTableQuery, - String selectAllFromMetadataTableQuery, String dropNamespaceTableQuery, String dropMetadataSchemaQuery) throws Exception { @@ -2009,7 +1981,6 @@ private void dropNamespace_WithOnlyNamespaceSchemaLeftForX_shouldDropSchemaAndNa PreparedStatement isNamespaceEmptyStatementMock = mock(PreparedStatement.class); PreparedStatement deleteFromNamespaceTablePrepStmt = mock(PreparedStatement.class); Statement selectAllFromNamespaceTablePrepStmt = mock(Statement.class); - Statement selectAllFromMetadataTablePrepStmt = mock(Statement.class); Statement dropNamespaceTableStmt = mock(Statement.class); Statement dropMetadataSchemaStmt = mock(Statement.class); when(dataSource.getConnection()).thenReturn(connection); @@ -2017,7 +1988,6 @@ private void dropNamespace_WithOnlyNamespaceSchemaLeftForX_shouldDropSchemaAndNa .thenReturn( dropNamespaceStmt, selectAllFromNamespaceTablePrepStmt, - selectAllFromMetadataTablePrepStmt, dropNamespaceTableStmt, dropMetadataSchemaStmt); // Mock for isNamespaceEmpty() check - returns empty ResultSet (namespace is empty) @@ -2033,10 +2003,6 @@ private void dropNamespace_WithOnlyNamespaceSchemaLeftForX_shouldDropSchemaAndNa new SelectNamespaceNameFromNamespaceTableResultSetMocker.Row(METADATA_SCHEMA)); when(selectAllFromNamespaceTablePrepStmt.executeQuery(anyString())).thenReturn(resultSet); - SQLException sqlException = mock(SQLException.class); - mockUndefinedTableError(rdbEngine, sqlException); - when(selectAllFromMetadataTablePrepStmt.execute(anyString())).thenThrow(sqlException); - // Act admin.dropNamespace(namespace); @@ -2046,7 +2012,6 @@ private void dropNamespace_WithOnlyNamespaceSchemaLeftForX_shouldDropSchemaAndNa verify(connection).prepareStatement(deleteFromNamespaceTableQuery); verify(deleteFromNamespaceTablePrepStmt).execute(); verify(selectAllFromNamespaceTablePrepStmt).executeQuery(selectAllFromNamespaceTableQuery); - verify(selectAllFromMetadataTablePrepStmt).execute(selectAllFromMetadataTableQuery); verify(dropNamespaceTableStmt).execute(dropNamespaceTableQuery); verify(dropMetadataSchemaStmt).execute(dropMetadataSchemaQuery); } @@ -4023,22 +3988,6 @@ private String prepareSqlForTableCheck(RdbEngine rdbEngine, String namespace, St 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 RdbEngineStrategy getRdbEngineStrategy(RdbEngine rdbEngine) { RdbEngineStrategy engine = RDB_ENGINES.get(rdbEngine); if (engine == null) { @@ -4203,18 +4152,31 @@ private void repairNamespace_forX_shouldWorkProperly( verify(mockedInsertNamespaceStatement2).execute(); } + @Test + public void repairNamespace_ForSqlite_withInvalidNamespaceName_ShouldThrowExecutionException() { + // Arrange + String namespace = "my$ns"; // contains namespace separator + + JdbcAdmin admin = createJdbcAdminFor(RdbEngine.SQLITE); + + // Act + // Assert + assertThatThrownBy(() -> admin.repairNamespace(namespace, Collections.emptyMap())) + .isInstanceOf(IllegalArgumentException.class); + } + @Test public void upgrade_ForMysql_ShouldInsertAllNamespacesFromMetadataTable() throws SQLException, ExecutionException { upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.MYSQL, "SELECT 1 FROM `" + METADATA_SCHEMA + "`.`metadata` LIMIT 1", + "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`", "SELECT 1 FROM `" + METADATA_SCHEMA + "`.`namespaces` LIMIT 1", ImmutableList.of("CREATE SCHEMA IF NOT EXISTS `" + METADATA_SCHEMA + "`"), "CREATE TABLE IF NOT EXISTS `" + METADATA_SCHEMA + "`.`namespaces`(`namespace_name` VARCHAR(128), PRIMARY KEY (`namespace_name`))", - "SELECT DISTINCT `full_table_name` FROM `" + METADATA_SCHEMA + "`.`metadata`", "INSERT INTO `" + METADATA_SCHEMA + "`.`namespaces` VALUES (?)"); } @@ -4224,12 +4186,12 @@ public void upgrade_ForPosgresql_ShouldInsertAllNamespacesFromMetadataTable() upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.POSTGRESQL, "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" LIMIT 1", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" LIMIT 1", ImmutableList.of("CREATE SCHEMA IF NOT EXISTS \"" + METADATA_SCHEMA + "\""), "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"namespaces\"(\"namespace_name\" VARCHAR(128), PRIMARY KEY (\"namespace_name\"))", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "INSERT INTO \"" + METADATA_SCHEMA + "\".\"namespaces\" VALUES (?)"); } @@ -4239,6 +4201,7 @@ public void upgrade_ForOracle_ShouldInsertAllNamespacesFromMetadataTable() upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.ORACLE, "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" FETCH FIRST 1 ROWS ONLY", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" FETCH FIRST 1 ROWS ONLY", ImmutableList.of( "CREATE USER \"" + METADATA_SCHEMA + "\" IDENTIFIED BY \"Oracle1234!@#$\"", @@ -4246,7 +4209,6 @@ public void upgrade_ForOracle_ShouldInsertAllNamespacesFromMetadataTable() "CREATE TABLE \"" + METADATA_SCHEMA + "\".\"namespaces\"(\"namespace_name\" VARCHAR2(128), PRIMARY KEY (\"namespace_name\"))", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "INSERT INTO \"" + METADATA_SCHEMA + "\".\"namespaces\" VALUES (?)"); } @@ -4256,12 +4218,12 @@ public void upgrade_ForSqlServer_ShouldInsertAllNamespacesFromMetadataTable() upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.SQL_SERVER, "SELECT TOP 1 1 FROM [" + METADATA_SCHEMA + "].[metadata]", + "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]", "SELECT TOP 1 1 FROM [" + METADATA_SCHEMA + "].[namespaces]", ImmutableList.of("CREATE SCHEMA [" + METADATA_SCHEMA + "]"), "CREATE TABLE [" + METADATA_SCHEMA + "].[namespaces]([namespace_name] VARCHAR(128), PRIMARY KEY ([namespace_name]))", - "SELECT DISTINCT [full_table_name] FROM [" + METADATA_SCHEMA + "].[metadata]", "INSERT INTO [" + METADATA_SCHEMA + "].[namespaces] VALUES (?)"); } @@ -4271,12 +4233,12 @@ public void upgrade_ForSqlite_ShouldInsertAllNamespacesFromMetadataTable() upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.SQLITE, "SELECT 1 FROM \"" + METADATA_SCHEMA + "$metadata\" LIMIT 1", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\"", "SELECT 1 FROM \"" + METADATA_SCHEMA + "$namespaces\" LIMIT 1", Collections.emptyList(), "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "$namespaces\"(\"namespace_name\" TEXT, PRIMARY KEY (\"namespace_name\"))", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "$metadata\"", "INSERT INTO \"" + METADATA_SCHEMA + "$namespaces\" VALUES (?)"); } @@ -4286,32 +4248,32 @@ public void upgrade_ForDb2_ShouldInsertAllNamespacesFromMetadataTable() upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine.DB2, "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"metadata\" LIMIT 1", + "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "SELECT 1 FROM \"" + METADATA_SCHEMA + "\".\"namespaces\" LIMIT 1", ImmutableList.of("CREATE SCHEMA \"" + METADATA_SCHEMA + "\""), "CREATE TABLE IF NOT EXISTS \"" + METADATA_SCHEMA + "\".\"namespaces\"(\"namespace_name\" VARCHAR(128) NOT NULL, PRIMARY KEY (\"namespace_name\"))", - "SELECT DISTINCT \"full_table_name\" FROM \"" + METADATA_SCHEMA + "\".\"metadata\"", "INSERT INTO \"" + METADATA_SCHEMA + "\".\"namespaces\" VALUES (?)"); } private void upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( RdbEngine rdbEngine, String tableMetadataExistStatement, + String getTableMetadataNamespacesStatement, String namespacesTableExistsStatement, List createMetadataNamespaceStatements, String createNamespaceTableStatement, - String getTableMetadataNamespacesStatement, String insertNamespaceStatement) throws SQLException, ExecutionException { // Arrange // Instantiate mocks Statement tableMetadataExistsStatementMock = mock(Statement.class); + Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); Statement namespacesTableExistsStatementMock = mock(Statement.class); List createMetadataNamespaceStatementsMock = prepareMockStatements(createMetadataNamespaceStatements.size()); Statement createNamespaceTableStatementMock = mock(Statement.class); - Statement getTableMetadataNamespacesStatementMock = mock(Statement.class); PreparedStatement insertNamespacePrepStmt1 = mock(PreparedStatement.class); PreparedStatement insertNamespacePrepStmt2 = mock(PreparedStatement.class); PreparedStatement insertNamespacePrepStmt3 = mock(PreparedStatement.class); @@ -4321,10 +4283,10 @@ private void upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( List statementsMock = ImmutableList.builder() .add(tableMetadataExistsStatementMock) + .add(getTableMetadataNamespacesStatementMock) .add(namespacesTableExistsStatementMock) .addAll(createMetadataNamespaceStatementsMock) .add(createNamespaceTableStatementMock) - .add(getTableMetadataNamespacesStatementMock) .build(); // Prepare calls @@ -4352,14 +4314,14 @@ private void upgrade_ForX_ShouldInsertAllNamespacesFromMetadataTable( // Assert verify(tableMetadataExistsStatementMock).execute(tableMetadataExistStatement); + verify(getTableMetadataNamespacesStatementMock) + .executeQuery(getTableMetadataNamespacesStatement); verify(namespacesTableExistsStatementMock).execute(namespacesTableExistsStatement); for (int i = 0; i < createMetadataNamespaceStatementsMock.size(); i++) { verify(createMetadataNamespaceStatementsMock.get(i)) .execute(createMetadataNamespaceStatements.get(i)); } verify(createNamespaceTableStatementMock).execute(createNamespaceTableStatement); - verify(getTableMetadataNamespacesStatementMock) - .executeQuery(getTableMetadataNamespacesStatement); verify(connection, times(3)).prepareStatement(insertNamespaceStatement); verify(insertNamespacePrepStmt1).setString(1, METADATA_SCHEMA); verify(insertNamespacePrepStmt2).setString(1, "ns2"); @@ -4396,7 +4358,7 @@ public void upsertIntoNamespacesTable_ForNonExistingNamespace_ShouldInsertNamesp .prepareStatement( "INSERT INTO " + rdbEngineStrategy.encloseFullTableName( - METADATA_SCHEMA, JdbcAdmin.NAMESPACES_TABLE) + METADATA_SCHEMA, NamespaceMetadataService.TABLE_NAME) + " VALUES (?)"); verify(insertStatement).setString(1, NAMESPACE); verify(insertStatement).execute(); @@ -4421,7 +4383,7 @@ public void upsertIntoNamespacesTable_ForExistingNamespace_ShouldNotThrowExcepti .prepareStatement( "INSERT INTO " + rdbEngineStrategy.encloseFullTableName( - METADATA_SCHEMA, JdbcAdmin.NAMESPACES_TABLE) + METADATA_SCHEMA, NamespaceMetadataService.TABLE_NAME) + " VALUES (?)"); verify(insertStatement).setString(1, NAMESPACE); verify(insertStatement).execute(); @@ -4640,7 +4602,7 @@ public void upsertIntoNamespacesTable_ForNonDuplicatedKeyException_ShouldThrowSq .prepareStatement( "INSERT INTO " + rdbEngineStrategy.encloseFullTableName( - METADATA_SCHEMA, JdbcAdmin.NAMESPACES_TABLE) + METADATA_SCHEMA, NamespaceMetadataService.TABLE_NAME) + " VALUES (?)"); verify(insertStatement).setString(1, NAMESPACE); verify(insertStatement).execute(); @@ -4862,12 +4824,12 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } 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; } @@ -4915,7 +4877,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; } @@ -4951,7 +4913,7 @@ public Object answer(InvocationOnMock invocation) throws Throwable { } SelectNamespaceNameFromNamespaceTableResultSetMocker.Row currentRow = rows.get(row); ResultSet mock = (ResultSet) invocation.getMock(); - when(mock.getString(JdbcAdmin.NAMESPACE_COL_NAMESPACE_NAME)) + when(mock.getString(NamespaceMetadataService.COL_NAMESPACE_NAME)) .thenReturn(currentRow.namespaceName); return true; } 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 716014dda..e5c18d203 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;