Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 64 additions & 51 deletions core/src/main/java/com/scalar/db/storage/jdbc/JdbcAdmin.java
Original file line number Diff line number Diff line change
Expand Up @@ -437,40 +437,43 @@ public TableMetadata getTableMetadata(String namespace, String table) throws Exe
TableMetadata.Builder builder = TableMetadata.newBuilder();
boolean tableExists = false;

try (Connection connection = dataSource.getConnection();
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);
try (Connection connection = dataSource.getConnection()) {
rdbEngine.setReadOnly(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);
}
}
}
}
Expand Down Expand Up @@ -507,6 +510,8 @@ public TableMetadata getImportTableMetadata(
}

try (Connection connection = dataSource.getConnection()) {
rdbEngine.setReadOnly(connection, true);

String catalogName = rdbEngine.getCatalogName(namespace);
String schemaName = rdbEngine.getSchemaName(namespace);

Expand Down Expand Up @@ -602,19 +607,22 @@ public Set<String> getNamespaceTableNames(String namespace) throws ExecutionExce
+ " WHERE "
+ enclose(METADATA_COL_FULL_TABLE_NAME)
+ " LIKE ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement preparedStatement =
connection.prepareStatement(selectTablesOfNamespaceStatement)) {
String prefix = namespace + ".";
preparedStatement.setString(1, prefix + "%");
try (ResultSet results = preparedStatement.executeQuery()) {
Set<String> tableNames = new HashSet<>();
while (results.next()) {
String tableName =
results.getString(METADATA_COL_FULL_TABLE_NAME).substring(prefix.length());
tableNames.add(tableName);
try (Connection connection = dataSource.getConnection()) {
rdbEngine.setReadOnly(connection, true);

try (PreparedStatement preparedStatement =
connection.prepareStatement(selectTablesOfNamespaceStatement)) {
String prefix = namespace + ".";
preparedStatement.setString(1, prefix + "%");
try (ResultSet results = preparedStatement.executeQuery()) {
Set<String> tableNames = new HashSet<>();
while (results.next()) {
String tableName =
results.getString(METADATA_COL_FULL_TABLE_NAME).substring(prefix.length());
tableNames.add(tableName);
}
return tableNames;
}
return tableNames;
}
} catch (SQLException e) {
// An exception will be thrown if the metadata table does not exist when executing the select
Expand All @@ -635,11 +643,14 @@ public boolean namespaceExists(String namespace) throws ExecutionException {
+ " WHERE "
+ enclose(NAMESPACE_COL_NAMESPACE_NAME)
+ " = ?";
try (Connection connection = dataSource.getConnection();
PreparedStatement statement = connection.prepareStatement(selectQuery)) {
statement.setString(1, namespace);
try (ResultSet resultSet = statement.executeQuery()) {
return resultSet.next();
try (Connection connection = dataSource.getConnection()) {
rdbEngine.setReadOnly(connection, true);

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
Expand Down Expand Up @@ -981,6 +992,8 @@ private String encloseFullTableName(String schema, String table) {
@Override
public Set<String> getNamespaceNames() throws ExecutionException {
try (Connection connection = dataSource.getConnection()) {
rdbEngine.setReadOnly(connection, true);

String selectQuery =
"SELECT * FROM " + encloseFullTableName(metadataSchema, NAMESPACES_TABLE);
Set<String> namespaces = new HashSet<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ public Optional<Result> get(Get get) throws ExecutionException {
Connection connection = null;
try {
connection = dataSource.getConnection();
rdbEngine.setReadOnly(connection, true);
return jdbcService.get(get, connection);
} catch (SQLException e) {
throw new ExecutionException(
Expand All @@ -97,6 +98,7 @@ public Scanner scan(Scan scan) throws ExecutionException {
Connection connection = null;
try {
connection = dataSource.getConnection();
rdbEngine.setReadOnly(connection, true);
return jdbcService.getScanner(scan, connection);
} catch (SQLException e) {
close(connection);
Expand Down
8 changes: 8 additions & 0 deletions core/src/main/java/com/scalar/db/storage/jdbc/JdbcUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ public static BasicDataSource initDataSource(
}
});

dataSource.setDefaultReadOnly(false);

dataSource.setMinIdle(config.getConnectionPoolMinIdle());
dataSource.setMaxIdle(config.getConnectionPoolMaxIdle());
dataSource.setMaxTotal(config.getConnectionPoolMaxTotal());
Expand All @@ -89,6 +91,9 @@ public static BasicDataSource initDataSourceForTableMetadata(
dataSource.setUrl(config.getJdbcUrl());
config.getUsername().ifPresent(dataSource::setUsername);
config.getPassword().ifPresent(dataSource::setPassword);

dataSource.setDefaultReadOnly(false);

dataSource.setMinIdle(config.getTableMetadataConnectionPoolMinIdle());
dataSource.setMaxIdle(config.getTableMetadataConnectionPoolMaxIdle());
dataSource.setMaxTotal(config.getTableMetadataConnectionPoolMaxTotal());
Expand All @@ -113,6 +118,9 @@ public static BasicDataSource initDataSourceForAdmin(
dataSource.setUrl(config.getJdbcUrl());
config.getUsername().ifPresent(dataSource::setUsername);
config.getPassword().ifPresent(dataSource::setPassword);

dataSource.setDefaultReadOnly(false);

dataSource.setMinIdle(config.getAdminConnectionPoolMinIdle());
dataSource.setMaxIdle(config.getAdminConnectionPoolMaxIdle());
dataSource.setMaxTotal(config.getAdminConnectionPoolMaxTotal());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import com.scalar.db.storage.jdbc.query.UpsertQuery;
import com.scalar.db.util.TimeRelatedColumnEncodingUtils;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.JDBCType;
import java.sql.ResultSet;
Expand Down Expand Up @@ -337,4 +338,9 @@ public TimestampTZColumn parseTimestampTZColumn(ResultSet resultSet, String colu
public RdbEngineTimeTypeStrategy<Integer, Long, Long, Long> getTimeTypeStrategy() {
return timeTypeEngine;
}

@Override
public void setReadOnly(Connection connection, boolean readOnly) {
// Do nothing. SQLite does not support read-only mode.
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.scalar.db.io.TimestampTZColumn;
import com.scalar.db.storage.jdbc.query.SelectQuery;
import com.scalar.db.storage.jdbc.query.UpsertQuery;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.JDBCType;
import java.sql.ResultSet;
Expand Down Expand Up @@ -228,4 +229,8 @@ default String getProjectionsSqlForSelectQuery(TableMetadata metadata, List<Stri
default void throwIfDuplicatedIndexWarning(SQLWarning warning) throws SQLException {
// Do nothing
}

default void setReadOnly(Connection connection, boolean readOnly) throws SQLException {
connection.setReadOnly(readOnly);
}
}
39 changes: 30 additions & 9 deletions core/src/test/java/com/scalar/db/storage/jdbc/JdbcAdminTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
Expand Down Expand Up @@ -273,6 +274,11 @@ private void getTableMetadata_forX_ShouldReturnTableMetadata(
.addSecondaryIndex("c4")
.build();
assertThat(actualMetadata).isEqualTo(expectedMetadata);
if (rdbEngine == RdbEngine.SQLITE) {
verify(connection, never()).setReadOnly(anyBoolean());
} else {
verify(connection).setReadOnly(true);
}
verify(connection).prepareStatement(expectedSelectStatements);
}

Expand Down Expand Up @@ -2191,6 +2197,11 @@ private void getNamespaceTables_forX_ShouldReturnTableNames(
Set<String> actualTableNames = admin.getNamespaceTableNames(namespace);

// Assert
if (rdbEngine == RdbEngine.SQLITE) {
verify(connection, never()).setReadOnly(anyBoolean());
} else {
verify(connection).setReadOnly(true);
}
verify(connection).prepareStatement(expectedSelectStatement);
assertThat(actualTableNames).containsExactly(table1, table2);
verify(preparedStatement).setString(1, namespace + ".%");
Expand Down Expand Up @@ -2259,6 +2270,11 @@ private void namespaceExists_forXWithExistingNamespace_ShouldReturnTrue(
// Assert
assertThat(admin.namespaceExists(namespace)).isTrue();

if (rdbEngine == RdbEngine.SQLITE) {
verify(connection, never()).setReadOnly(anyBoolean());
} else {
verify(connection).setReadOnly(true);
}
verify(selectStatement).executeQuery();
verify(connection).prepareStatement(expectedSelectStatement);
verify(selectStatement).setString(1, namespace);
Expand Down Expand Up @@ -2949,21 +2965,25 @@ private void getNamespaceNames_forX_ShouldReturnNamespaceNames(
Set<String> actualNamespaceNames = admin.getNamespaceNames();

// Assert
if (rdbEngine == RdbEngine.SQLITE) {
verify(connection, never()).setReadOnly(anyBoolean());
} else {
verify(connection).setReadOnly(true);
}
verify(connection).prepareStatement(expectedSelectStatement);
verify(mockPreparedStatement).executeQuery();
assertThat(actualNamespaceNames).containsOnly(namespace1, namespace2);
}

@Test
public void getImportTableMetadata_ForX_ShouldWorkProperly()
@ParameterizedTest
@EnumSource(RdbEngine.class)
public void getImportTableMetadata_ForX_ShouldWorkProperly(RdbEngine rdbEngine)
throws SQLException, ExecutionException {
for (RdbEngine rdbEngine : RDB_ENGINES.keySet()) {
if (rdbEngine.equals(RdbEngine.SQLITE)) {
getImportTableMetadata_ForSQLite_ShouldThrowUnsupportedOperationException(rdbEngine);
} else {
getImportTableMetadata_ForOtherThanSQLite_ShouldWorkProperly(
rdbEngine, prepareSqlForTableCheck(rdbEngine, NAMESPACE, TABLE));
}
if (rdbEngine.equals(RdbEngine.SQLITE)) {
getImportTableMetadata_ForSQLite_ShouldThrowUnsupportedOperationException(rdbEngine);
} else {
getImportTableMetadata_ForOtherThanSQLite_ShouldWorkProperly(
rdbEngine, prepareSqlForTableCheck(rdbEngine, NAMESPACE, TABLE));
}
}

Expand Down Expand Up @@ -3030,6 +3050,7 @@ public Boolean answer(InvocationOnMock invocation) {
.execute(expectedCheckTableExistStatement);
assertThat(actual.getPartitionKeyNames()).hasSameElementsAs(ImmutableSet.of("pk1", "pk2"));
assertThat(actual.getColumnDataTypes()).containsExactlyEntriesOf(expectedColumns);
verify(connection).setReadOnly(true);
verify(rdbEngineStrategy)
.getDataTypeForScalarDb(
any(JDBCType.class),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ public void whenGetOperationExecuted_shouldCallJdbcService() throws Exception {
jdbcDatabase.get(get);

// Assert
verify(connection).setReadOnly(true);
verify(jdbcService).get(any(), any());
verify(connection).close();
}
Expand All @@ -89,6 +90,7 @@ public void whenGetOperationExecuted_shouldCallJdbcService() throws Exception {
jdbcDatabase.get(get);
})
.isInstanceOf(ExecutionException.class);
verify(connection).setReadOnly(true);
verify(connection).close();
}

Expand All @@ -104,6 +106,7 @@ public void whenScanOperationExecutedAndScannerClosed_shouldCallJdbcService() th
scanner.close();

// Assert
verify(connection).setReadOnly(true);
verify(jdbcService).getScanner(any(), any());
verify(connection).close();
}
Expand All @@ -122,6 +125,7 @@ public void whenScanOperationExecutedAndScannerClosed_shouldCallJdbcService() th
jdbcDatabase.scan(scan);
})
.isInstanceOf(ExecutionException.class);
verify(connection).setReadOnly(true);
verify(connection).close();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public void initDataSource_NonTransactional_ShouldReturnProperDataSource() throw
assertThat(dataSource.getAutoCommitOnReturn()).isEqualTo(true);
assertThat(dataSource.getDefaultTransactionIsolation())
.isEqualTo(Connection.TRANSACTION_SERIALIZABLE);
assertThat(dataSource.getDefaultReadOnly()).isFalse();

assertThat(dataSource.getMinIdle()).isEqualTo(10);
assertThat(dataSource.getMaxIdle()).isEqualTo(20);
Expand Down Expand Up @@ -109,6 +110,7 @@ public void initDataSource_Transactional_ShouldReturnProperDataSource() throws S
assertThat(dataSource.getAutoCommitOnReturn()).isEqualTo(false);
assertThat(dataSource.getDefaultTransactionIsolation())
.isEqualTo(Connection.TRANSACTION_READ_COMMITTED);
assertThat(dataSource.getDefaultReadOnly()).isFalse();

assertThat(dataSource.getMinIdle()).isEqualTo(30);
assertThat(dataSource.getMaxIdle()).isEqualTo(40);
Expand Down Expand Up @@ -180,6 +182,8 @@ public void initDataSourceForTableMetadata_ShouldReturnProperDataSource() throws
assertThat(tableMetadataDataSource.getUsername()).isEqualTo("user");
assertThat(tableMetadataDataSource.getPassword()).isEqualTo("oracle");

assertThat(tableMetadataDataSource.getDefaultReadOnly()).isFalse();

assertThat(tableMetadataDataSource.getMinIdle()).isEqualTo(100);
assertThat(tableMetadataDataSource.getMaxIdle()).isEqualTo(200);
assertThat(tableMetadataDataSource.getMaxTotal()).isEqualTo(300);
Expand Down Expand Up @@ -212,6 +216,8 @@ public void initDataSourceForAdmin_ShouldReturnProperDataSource() throws SQLExce
assertThat(adminDataSource.getUsername()).isEqualTo("user");
assertThat(adminDataSource.getPassword()).isEqualTo("sqlserver");

assertThat(adminDataSource.getDefaultReadOnly()).isFalse();

assertThat(adminDataSource.getMinIdle()).isEqualTo(100);
assertThat(adminDataSource.getMaxIdle()).isEqualTo(200);
assertThat(adminDataSource.getMaxTotal()).isEqualTo(300);
Expand Down