diff --git a/liquibase-core/src/main/java/liquibase/database/AbstractJdbcDatabase.java b/liquibase-core/src/main/java/liquibase/database/AbstractJdbcDatabase.java index cf447275d9..1060f4019d 100644 --- a/liquibase-core/src/main/java/liquibase/database/AbstractJdbcDatabase.java +++ b/liquibase-core/src/main/java/liquibase/database/AbstractJdbcDatabase.java @@ -30,7 +30,8 @@ import liquibase.statement.SequenceCurrentValueFunction; import liquibase.statement.SequenceNextValueFunction; import liquibase.statement.SqlStatement; -import liquibase.statement.core.*; +import liquibase.statement.core.GetViewDefinitionStatement; +import liquibase.statement.core.RawCallStatement; import liquibase.structure.DatabaseObject; import liquibase.structure.core.*; import liquibase.util.ISODateFormat; @@ -763,7 +764,7 @@ public void dropDatabaseObjects(final CatalogAndSchema schemaToDrop) throws Liqu typesToInclude.remove(PrimaryKey.class); typesToInclude.remove(UniqueConstraint.class); - if (supportsForeignKeyDisable() || getShortName().equals("postgresql")) { + if (supportsForeignKeyDisable() || getShortName().equals("postgresql") || getShortName().equals("oracle")) { //We do not remove ForeignKey because they will be disabled and removed as parts of tables. // Postgress is treated as if we can disable foreign keys because we can't drop // the foreign keys of a partitioned table, as discovered in diff --git a/liquibase-core/src/main/java/liquibase/diff/output/changelog/DiffToChangeLog.java b/liquibase-core/src/main/java/liquibase/diff/output/changelog/DiffToChangeLog.java index 736f192bf6..1de56cc3be 100644 --- a/liquibase-core/src/main/java/liquibase/diff/output/changelog/DiffToChangeLog.java +++ b/liquibase-core/src/main/java/liquibase/diff/output/changelog/DiffToChangeLog.java @@ -393,6 +393,63 @@ public String toString(String obj) { return rs; } + private List> queryForReferencePartitionedDependenciesOracle(Executor executor, List schemas) + throws DatabaseException { + List> rs = null; + try { + if (tryDbaDependencies) { + rs = executor.queryForList(new RawSqlStatement("SELECT UNIQUE\n" + + " referencer.OWNER AS OWNER,\n" + + " referencer.TABLE_NAME AS NAME,\n" + + " referenced.OWNER AS REFERENCED_OWNER,\n" + + " referenced.TABLE_NAME AS REFERENCED_NAME\n" + + "FROM\n" + + " DBA_CONSTRAINTS referencer\n" + + " JOIN\n" + + " DBA_CONSTRAINTS referenced\n" + + " ON referencer.R_CONSTRAINT_NAME = referenced.CONSTRAINT_NAME\n" + + " AND referencer.R_OWNER = referenced.OWNER\n" + + "WHERE referencer.CONSTRAINT_TYPE = 'R' and referenced.OWNER != 'SYS' " + + "AND (" + StringUtils.join(schemas, " OR ", + (StringUtils.StringUtilsFormatter) obj -> "referencer.OWNER='" + obj + "'" + ) + ")")); + } else { + rs = executor.queryForList(new RawSqlStatement("SELECT UNIQUE\n" + + " referencer.OWNER AS OWNER,\n" + + " referencer.TABLE_NAME AS NAME,\n" + + " referenced.OWNER AS REFERENCED_OWNER,\n" + + " referenced.TABLE_NAME AS REFERENCED_NAME\n" + + "FROM\n" + + " ALL_CONSTRAINTS referencer\n" + + " JOIN\n" + + " ALL_CONSTRAINTS referenced\n" + + " ON referencer.R_CONSTRAINT_NAME = referenced.CONSTRAINT_NAME\n" + + " AND referencer.R_OWNER = referenced.OWNER\n" + + "WHERE referencer.CONSTRAINT_TYPE = 'R' and referenced.OWNER != 'SYS' " + + "AND (" + StringUtils.join(schemas, " OR ", + (StringUtils.StringUtilsFormatter) obj -> "referencer.OWNER='" + obj + "'" + ) + ")")); + } + } catch (DatabaseException dbe) { + // + // If our exception is for something other than a missing table/view + // then we just re-throw the exception + // else if we can't see ALL_TAB_PARTITIONS then we also re-throw + // to stop the recursion + // + String message = dbe.getMessage(); + if (!message.contains("ORA-00942: table or view does not exist")) { + throw new DatabaseException(dbe); + } else if (!tryDbaDependencies) { + throw new DatabaseException(dbe); + } + logger.warning("Unable to query DBA_TAB_PARTITIONS table. Switching to ALL_TAB_PARTITIONS"); + tryDbaDependencies = false; + return queryForReferencePartitionedDependenciesOracle(executor, schemas); + } + return rs; + } + /** * Used by {@link #sortMissingObjects(Collection, Database)} to determine whether to go into the sorting logic. */ @@ -436,24 +493,10 @@ public String toString(String obj) { } } else if (database instanceof OracleDatabase) { Executor executor = ExecutorService.getInstance().getExecutor(database); - List> rs = queryForDependenciesOracle(executor, schemas); - for (Map row : rs) { - String tabName = null; - if (tryDbaDependencies) { - tabName = - StringUtils.trimToNull((String) row.get("OWNER")) + "." + - StringUtils.trimToNull((String) row.get("NAME")); - } else { - tabName = - StringUtils.trimToNull((String) row.get("REFERENCED_OWNER")) + "." + - StringUtils.trimToNull((String) row.get("NAME")); - } - String bName = - StringUtils.trimToNull((String) row.get("REFERENCED_OWNER")) + "." + - StringUtils.trimToNull((String) row.get("REFERENCED_NAME")); - - graph.add(bName, tabName); - } + List> declaredDependencies = queryForDependenciesOracle(executor, schemas); + addOracleDependencies(graph, declaredDependencies); + List> referencedPartDependencies = queryForReferencePartitionedDependenciesOracle(executor, schemas); + addOracleDependencies(graph, referencedPartDependencies); } else if (database instanceof MSSQLDatabase && database.getDatabaseMajorVersion() >= 9) { Executor executor = ExecutorService.getInstance().getExecutor(database); String sql = "select object_schema_name(referencing_id) as referencing_schema_name, object_name(referencing_id) as referencing_name, object_name(referenced_id) as referenced_name, object_schema_name(referenced_id) as referenced_schema_name from sys.sql_expression_dependencies depz where (" + StringUtils.join(schemas, " OR ", new StringUtils.StringUtilsFormatter() { @@ -567,6 +610,26 @@ public String toString(String obj) { } } + private void addOracleDependencies(DependencyUtil.DependencyGraph graph, List> dependenciesResultSet) { + for (Map row : dependenciesResultSet) { + String tabName = null; + if (tryDbaDependencies) { + tabName = + StringUtils.trimToNull((String) row.get("OWNER")) + "." + + StringUtils.trimToNull((String) row.get("NAME")); + } else { + tabName = + StringUtils.trimToNull((String) row.get("REFERENCED_OWNER")) + "." + + StringUtils.trimToNull((String) row.get("NAME")); + } + String bName = + StringUtils.trimToNull((String) row.get("REFERENCED_OWNER")) + "." + + StringUtils.trimToNull((String) row.get("REFERENCED_NAME")); + + graph.add(bName, tabName); + } + } + private String queryForDependenciesPostgreSql(List schemas){ //SQL adapted from https://wiki.postgresql.org/wiki/Pg_depend_display return "WITH RECURSIVE preference AS (\n" +