Skip to content

Commit

Permalink
fix: correctly report support for catalog and schema (#372)
Browse files Browse the repository at this point in the history
* fix null shanpshotId when run Liquibase harness tests

* Add recursive check for snapshotId presence

* fix: register correct schema and catalog support

Spanner supports both Catalog and Schema (although each database
contains at most one catalog). Our Liquibase implementation should
indicate this correctly, and also correctly indicate that we do
not want to include the default schema in queries and other object
IDs. This would otherwise generate invalid object identifiers in the
form `.my_table` (as the default schema name is an empty string).

---------

Co-authored-by: Knut Olav Løite <[email protected]>
  • Loading branch information
vladimirevd and olavloite authored Nov 28, 2024
1 parent 0bb0ae2 commit 876d6ef
Show file tree
Hide file tree
Showing 4 changed files with 256 additions and 132 deletions.
25 changes: 24 additions & 1 deletion src/main/java/liquibase/ext/spanner/CloudSpanner.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
import com.google.cloud.spanner.jdbc.CloudSpannerJdbcConnection;
import java.sql.DriverManager;
import java.sql.SQLException;
import liquibase.Scope;
import liquibase.database.AbstractJdbcDatabase;
import liquibase.database.DatabaseConnection;
import liquibase.database.OfflineConnection;
import liquibase.database.jvm.JdbcConnection;

public class CloudSpanner extends AbstractJdbcDatabase implements ICloudSpanner {
Expand Down Expand Up @@ -89,7 +91,18 @@ protected String getConnectionSchemaName() {
if (getConnection() == null) {
return null;
}
return defaultSchemaName;
if (getConnection() instanceof OfflineConnection) {
return ((OfflineConnection) getConnection()).getSchema();
}
if (!(getConnection() instanceof JdbcConnection)) {
return defaultSchemaName;
}
try {
return ((JdbcConnection) getConnection()).getUnderlyingConnection().getSchema();
} catch (SQLException e) {
Scope.getCurrentScope().getLog(getClass()).info("Error getting default schema", e);
}
return null;
}

@Override
Expand Down Expand Up @@ -166,6 +179,11 @@ public boolean supportsSequences() {

@Override
public boolean supportsCatalogs() {
return true;
}

@Override
public boolean getOutputDefaultCatalog() {
return false;
}

Expand All @@ -176,6 +194,11 @@ public boolean isCaseSensitive() {

@Override
public boolean supportsSchemas() {
return true;
}

@Override
public boolean getOutputDefaultSchema() {
return false;
}

Expand Down
262 changes: 132 additions & 130 deletions src/test/java/liquibase/ext/spanner/AbstractMockServerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -156,136 +156,138 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, Re
}

private static void registerDefaultResults() {
for (String schemaAndCatalog : new String[] {"%", ""}) {
// Register metadata results for Liquibase tables.
mockSpanner.putStatementResult(
StatementResult.query(
Statement.newBuilder(JdbcMetadataQueries.GET_TABLES)
.bind("p1")
.to(schemaAndCatalog) // Catalog
.bind("p2")
.to(schemaAndCatalog) // Schema
.bind("p3")
.to("DATABASECHANGELOG")
.bind("p4")
.to("TABLE") // Liquibase searches for tables
.bind("p5")
.to("NON_EXISTENT_TYPE") // This is a trick in the JDBC driver to simplify the query
.build(),
JdbcMetadataQueries.createGetTablesResultSet(ImmutableList.of("DATABASECHANGELOG"))));
mockSpanner.putStatementResult(
StatementResult.query(
Statement.newBuilder(JdbcMetadataQueries.GET_COLUMNS)
.bind("p1")
.to(schemaAndCatalog) // Catalog
.bind("p2")
.to(schemaAndCatalog) // Schema
.bind("p3")
.to("DATABASECHANGELOG")
.bind("p4")
.to("%") // All column names
.build(),
JdbcMetadataQueries.createGetColumnsResultSet(
ImmutableList.of(
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"ID",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"AUTHOR",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"FILENAME",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DATEEXECUTED",
Types.TIMESTAMP,
"TIMESTAMP",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"ORDEREXECUTED",
Types.BIGINT,
"INT64",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"EXECTYPE",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"MD5SUM",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DESCRIPTION",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"COMMENTS",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"TAG",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"LIQUIBASE",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"CONTEXTS",
Types.NVARCHAR,
"STRING",
255,
DatabaseMetaData.columnNullable),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"LABELS",
Types.NVARCHAR,
"STRING",
255,
DatabaseMetaData.columnNullable),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DEPLOYMENT_ID",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls)))));
}
String catalog = "";
String schema = "";

// Register metadata results for Liquibase tables.
mockSpanner.putStatementResult(
StatementResult.query(
Statement.newBuilder(JdbcMetadataQueries.GET_TABLES)
.bind("p1")
.to(catalog) // Catalog
.bind("p2")
.to(schema) // Schema
.bind("p3")
.to("DATABASECHANGELOG")
.bind("p4")
.to("TABLE") // Liquibase searches for tables
.bind("p5")
.to("NON_EXISTENT_TYPE") // This is a trick in the JDBC driver to simplify the query
.build(),
JdbcMetadataQueries.createGetTablesResultSet(
ImmutableList.of("DATABASECHANGELOG"))));
mockSpanner.putStatementResult(
StatementResult.query(
Statement.newBuilder(JdbcMetadataQueries.GET_COLUMNS)
.bind("p1")
.to(catalog) // Catalog
.bind("p2")
.to(schema) // Schema
.bind("p3")
.to("DATABASECHANGELOG")
.bind("p4")
.to("%") // All column names
.build(),
JdbcMetadataQueries.createGetColumnsResultSet(
ImmutableList.of(
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"ID",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"AUTHOR",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"FILENAME",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DATEEXECUTED",
Types.TIMESTAMP,
"TIMESTAMP",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"ORDEREXECUTED",
Types.BIGINT,
"INT64",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"EXECTYPE",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"MD5SUM",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DESCRIPTION",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"COMMENTS",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"TAG",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"LIQUIBASE",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"CONTEXTS",
Types.NVARCHAR,
"STRING",
255,
DatabaseMetaData.columnNullable),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"LABELS",
Types.NVARCHAR,
"STRING",
255,
DatabaseMetaData.columnNullable),
new JdbcMetadataQueries.ColumnMetaData(
"DATABASECHANGELOG",
"DEPLOYMENT_ID",
Types.NVARCHAR,
"STRING",
0,
DatabaseMetaData.columnNoNulls)))));

// Register results for an empty Liquibase database.
mockSpanner.putStatementResult(
Expand Down
Loading

0 comments on commit 876d6ef

Please sign in to comment.