diff --git a/liquibase-core/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java b/liquibase-core/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java index adfea1077fd..4ddbb06d9da 100644 --- a/liquibase-core/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java +++ b/liquibase-core/src/main/java/liquibase/snapshot/JdbcDatabaseSnapshot.java @@ -1029,8 +1029,8 @@ public List fastFetchQuery() throws SQLException, DatabaseException { String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, schema, ((table == null) ? - SQL_FILTER_MATCH_ALL : table), new String[]{"TABLE"})); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema), ((table == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(table)), new String[]{"TABLE"})); } @Override @@ -1049,7 +1049,7 @@ public List bulkFetchQuery() throws SQLException, DatabaseException { String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, schema, SQL_FILTER_MATCH_ALL, new String[]{"TABLE"})); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema), SQL_FILTER_MATCH_ALL, new String[]{"TABLE"})); } private List queryMssql(CatalogAndSchema catalogAndSchema, String tableName) throws DatabaseException, SQLException { @@ -1133,8 +1133,8 @@ private List queryDb2Zos(CatalogAndSchema catalogAndSchema, String ta private List queryPostgres(CatalogAndSchema catalogAndSchema, String tableName) throws SQLException { String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, schema, ((tableName == null) ? - SQL_FILTER_MATCH_ALL : tableName), new String[]{"TABLE", "PARTITIONED TABLE"})); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema), ((tableName == null) ? + SQL_FILTER_MATCH_ALL : escapeForLike(tableName)), new String[]{"TABLE", "PARTITIONED TABLE"})); } }); @@ -1186,8 +1186,8 @@ public List fastFetchQuery() throws SQLException, DatabaseException { String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, schema, ((view == null) ? SQL_FILTER_MATCH_ALL - : view), new String[]{"VIEW"})); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema), ((view == null) ? SQL_FILTER_MATCH_ALL + : escapeForLike(view)), new String[]{"VIEW"})); } @Override @@ -1200,7 +1200,7 @@ public List bulkFetchQuery() throws SQLException, DatabaseException { String catalog = ((AbstractJdbcDatabase) database).getJdbcCatalogName(catalogAndSchema); String schema = ((AbstractJdbcDatabase) database).getJdbcSchemaName(catalogAndSchema); - return extract(databaseMetaData.getTables(catalog, schema, SQL_FILTER_MATCH_ALL, new String[]{"VIEW"})); + return extract(databaseMetaData.getTables(catalog, escapeForLike(schema), SQL_FILTER_MATCH_ALL, new String[]{"VIEW"})); } @@ -1698,4 +1698,13 @@ private String getAllCatalogsStringScratchData() { return (String) JdbcDatabaseSnapshot.this.getScratchData(ALL_CATALOGS_STRING_SCRATCH_KEY); } + private String escapeForLike(String string) { + if (string == null) { + return null; + } + return string + .replace("%", "\\%") + .replace("_", "\\_"); + } + } diff --git a/liquibase-integration-tests/src/test/groovy/liquibase/snapshot/JdbcDatabaseSnapshotTest.groovy b/liquibase-integration-tests/src/test/groovy/liquibase/snapshot/JdbcDatabaseSnapshotTest.groovy new file mode 100644 index 00000000000..4499199a1bd --- /dev/null +++ b/liquibase-integration-tests/src/test/groovy/liquibase/snapshot/JdbcDatabaseSnapshotTest.groovy @@ -0,0 +1,49 @@ +package liquibase.snapshot + +import liquibase.Scope +import liquibase.database.DatabaseFactory +import liquibase.database.jvm.JdbcConnection +import liquibase.extension.testing.testsystem.DatabaseTestSystem +import liquibase.extension.testing.testsystem.TestSystemFactory +import liquibase.statement.SqlStatement +import liquibase.statement.core.RawSqlStatement +import liquibase.structure.core.Table +import liquibase.structure.core.View +import org.junit.Rule +import spock.lang.Shared +import spock.lang.Specification +import spock.lang.Unroll + +import java.sql.Connection + +class JdbcDatabaseSnapshotTest extends Specification { + + @Rule + public DatabaseTestSystem h2 = Scope.currentScope.getSingleton(TestSystemFactory).getTestSystem("h2") + + @Unroll + def "getTables and getViews works with underscores in schema names"() { + when: + def connection = h2.getConnection() + def db = DatabaseFactory.instance.findCorrectDatabaseImplementation(new JdbcConnection(connection)) + db.execute([ + new RawSqlStatement("create schema if not exists \"TEST_SCHEMA\""), + new RawSqlStatement("create schema if not exists \"TEST-SCHEMA\""), + new RawSqlStatement("create table if not exists \"TEST-SCHEMA\".test_table (id int)"), + new RawSqlStatement("create view if not exists \"TEST-SCHEMA\".test_view as select * from \"TEST-SCHEMA\".test_table"), + ] as SqlStatement[], null) + + then: + SnapshotGeneratorFactory.instance.has(new Table(null, "TEST-SCHEMA", "TEST_TABLE"), db) + !SnapshotGeneratorFactory.instance.has(new Table(null, "TEST_SCHEMA", "TEST_TABLE"), db) + + SnapshotGeneratorFactory.instance.has(new View(null, "TEST-SCHEMA", "TEST_VIEW"), db) + !SnapshotGeneratorFactory.instance.has(new View(null, "TEST_SCHEMA", "TEST_VIEW"), db) + + cleanup: + db.execute([ + new RawSqlStatement("drop schema \"test_schema\" if exists"), + new RawSqlStatement("drop schema \"test-schema\" if exists"), + ] as SqlStatement[], null) + } +}