Skip to content

Commit

Permalink
feat: Implements skipObjectSorting option in GenerateChangelog/DiffCh…
Browse files Browse the repository at this point in the history
…angelog

* adds informative message if we reach a StackOverflow.
* Sonar Cleanups
  • Loading branch information
filipelautert committed Feb 2, 2024
1 parent d2455ba commit 2e87acd
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception {
Scope.getCurrentScope().addMdcValue(MdcKey.DIFF_CHANGELOG_FILE, changeLogFile);
referenceDatabase.setObjectQuotingStrategy(ObjectQuotingStrategy.QUOTE_ALL_OBJECTS);

DiffToChangeLog changeLogWriter = createDiffToChangeLogObject(diffResult, diffOutputControl);
DiffToChangeLog changeLogWriter = createDiffToChangeLogObject(diffResult, diffOutputControl, false);
changeLogWriter.setChangeSetContext(commandScope.getArgumentValue(CONTEXTS_ARG));
changeLogWriter.setChangeSetLabels(commandScope.getArgumentValue(LABEL_FILTER_ARG));
changeLogWriter.setChangeSetAuthor(commandScope.getArgumentValue(AUTHOR_ARG));
Expand Down Expand Up @@ -114,8 +114,8 @@ public void validate(CommandScope commandScope) throws CommandValidationExceptio
commandScope.addArgumentValue(DiffCommandStep.FORMAT_ARG, "none");
}

protected DiffToChangeLog createDiffToChangeLogObject(DiffResult diffResult, DiffOutputControl diffOutputControl) {
return new DiffToChangeLog(diffResult, diffOutputControl);
protected DiffToChangeLog createDiffToChangeLogObject(DiffResult diffResult, DiffOutputControl diffOutputControl, boolean skipObjectSorting) {
return new DiffToChangeLog(diffResult, diffOutputControl, skipObjectSorting);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ public class GenerateChangelogCommandStep extends AbstractCommandStep {
public static final CommandArgumentDefinition<String> REFERENCE_LIQUIBASE_SCHEMA_NAME_ARG;
public static final CommandArgumentDefinition<String> REFERENCE_LIQUIBASE_CATALOG_NAME_ARG;

public static final CommandArgumentDefinition<Boolean> SKIP_OBJECT_SORTING;

static {
final CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class)
Expand Down Expand Up @@ -88,6 +90,10 @@ public class GenerateChangelogCommandStep extends AbstractCommandStep {
.hidden().build();
REFERENCE_LIQUIBASE_CATALOG_NAME_ARG = builder.argument("referenceLiquibaseCatalogName", String.class)
.hidden().build();
SKIP_OBJECT_SORTING = builder.argument("skipObjectSorting", Boolean.class)
.defaultValue(false)
.description("When true will skip object sorting. This can be useful on databases that have a lot of packages/procedures that are " +
"linked to each other").build();
}

@Override
Expand Down Expand Up @@ -119,7 +125,7 @@ public void run(CommandResultsBuilder resultsBuilder) throws Exception {

DiffResult diffResult = (DiffResult) resultsBuilder.getResult(DiffCommandStep.DIFF_RESULT.getName());

DiffToChangeLog changeLogWriter = new DiffToChangeLog(diffResult, diffOutputControl);
DiffToChangeLog changeLogWriter = new DiffToChangeLog(diffResult, diffOutputControl, commandScope.getArgumentValue(SKIP_OBJECT_SORTING));

changeLogWriter.setChangeSetAuthor(commandScope.getArgumentValue(AUTHOR_ARG));
if (commandScope.getArgumentValue(CONTEXT_ARG) != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
import java.util.*;
import java.util.stream.Collectors;

import static liquibase.command.core.GenerateChangelogCommandStep.SKIP_OBJECT_SORTING;

public class DiffToChangeLog {

public static final String ORDER_ATTRIBUTE = "order";
Expand All @@ -64,24 +66,37 @@ public class DiffToChangeLog {
private final DiffOutputControl diffOutputControl;
private boolean tryDbaDependencies = true;

private boolean skipObjectSorting = false;

private static final Set<Class> loggedOrderFor = new HashSet<>();

/**
* Creates a new DiffToChangeLog with the given DiffResult and default DiffOutputControl
* @param diffResult the DiffResult to convert to a ChangeLog
* @param diffOutputControl the DiffOutputControl to use to control the output
* @param skipObjectSorting if true, will skip dependency object sorting. This can be useful on databases that have a lot of packages/procedures that are linked to each other
*/
public DiffToChangeLog(DiffResult diffResult, DiffOutputControl diffOutputControl, boolean skipObjectSorting) {
this(diffResult, diffOutputControl);
this.skipObjectSorting = skipObjectSorting;
}

public DiffToChangeLog(DiffResult diffResult, DiffOutputControl diffOutputControl) {
this.diffResult = diffResult;
this.diffOutputControl = diffOutputControl;
respectSchemaAndCatalogCaseIfNeeded(diffOutputControl);
}

public DiffToChangeLog(DiffOutputControl diffOutputControl) {
this.diffOutputControl = diffOutputControl;
}

private void respectSchemaAndCatalogCaseIfNeeded(DiffOutputControl diffOutputControl) {
if (this.diffResult.getComparisonSnapshot().getDatabase() instanceof AbstractDb2Database) {
diffOutputControl.setRespectSchemaAndCatalogCase(true);
}
}

public DiffToChangeLog(DiffOutputControl diffOutputControl) {
this.diffOutputControl = diffOutputControl;
}

public void setDiffResult(DiffResult diffResult) {
this.diffResult = diffResult;
}
Expand Down Expand Up @@ -346,10 +361,10 @@ public List<ChangeSet> generateChangeSets() {
// drop FK goes first
//
private List<ChangeSet> bringDropFKToTop(List<ChangeSet> changeSets) {
List<ChangeSet> dropFk = changeSets.stream().filter(cs -> {
return cs.getChanges().stream().anyMatch(ch -> ch instanceof DropForeignKeyConstraintChange);
}).collect(Collectors.toList());
if (dropFk == null || dropFk.isEmpty()) {
List<ChangeSet> dropFk = changeSets.stream().filter(cs ->
cs.getChanges().stream().anyMatch(DropForeignKeyConstraintChange.class::isInstance)
).collect(Collectors.toList());
if (dropFk.isEmpty()) {
return changeSets;
}
List<ChangeSet> returnList = new ArrayList<>();
Expand Down Expand Up @@ -472,6 +487,10 @@ private List<DatabaseObject> sortObjects(final String type, Collection<DatabaseO
}
} catch (DatabaseException e) {
Scope.getCurrentScope().getLog(getClass()).fine("Cannot get object dependencies: " + e.getMessage());
} catch (StackOverflowError e) {
Scope.getCurrentScope().getLog(getClass()).warning("You have too many or recursive database object dependencies! " +
"Liquibase is going to ignore dependency sorting and resume processing. To skip this message " +
"(and gain a lot of processing time) use flag " + SKIP_OBJECT_SORTING.getName(), e);
}
}
return new ArrayList<>(objects);
Expand All @@ -496,9 +515,7 @@ private List<DatabaseObject> sortObjects(final String type, Collection<DatabaseO
// to stop the recursion
//
String message = dbe.getMessage();
if (!message.contains("ORA-00942")) {
throw new DatabaseException(dbe);
} else if (!tryDbaDependencies) {
if (!message.contains("ORA-00942") || !tryDbaDependencies) {
throw new DatabaseException(dbe);
}
Scope.getCurrentScope().getLog(getClass()).warning("Unable to query DBA_DEPENDENCIES table. Switching to USER_DEPENDENCIES");
Expand All @@ -512,6 +529,9 @@ private List<DatabaseObject> sortObjects(final String type, Collection<DatabaseO
* Used by {@link #sortMissingObjects(Collection, Database)} to determine whether to go into the sorting logic.
*/
protected boolean supportsSortingObjects(Database database) {
if (this.skipObjectSorting) {
return false;
}
return (database instanceof AbstractDb2Database) || (database instanceof MSSQLDatabase) || (database instanceof
OracleDatabase) || database instanceof PostgresDatabase;
}
Expand Down Expand Up @@ -745,6 +765,7 @@ protected List<Class<? extends DatabaseObject>> getOrderedOutputTypes(Class<? ex

private void addToChangeSets(Change[] changes, List<ChangeSet> changeSets, ObjectQuotingStrategy quotingStrategy, String created) {
if (changes != null) {

String csContext = this.changeSetContext;

if (diffOutputControl.getContext() != null) {
Expand All @@ -769,6 +790,7 @@ private void addToChangeSets(Change[] changes, List<ChangeSet> changeSets, Objec
} else {
ChangeSet changeSet = new ChangeSet(generateId(changes), getChangeSetAuthor(), false, false, this.changeSetPath, csContext,
null, true, quotingStrategy, null);

changeSet.setCreated(created);
if (diffOutputControl.getLabels() != null) {
changeSet.setLabels(diffOutputControl.getLabels());
Expand Down Expand Up @@ -985,20 +1007,10 @@ private void checkForCycleInDependencies(Class<? extends ChangeGenerator> genera
}
}


private Node getNode(Class<? extends DatabaseObject> type) {
Node node = allNodes.get(type);
if (node == null) {
node = new Node(type);
}
return node;
}


static class Node {
public final Class<? extends DatabaseObject> type;
public final HashSet<Edge> inEdges;
public final HashSet<Edge> outEdges;
public final Set<Edge> inEdges;
public final Set<Edge> outEdges;

public Node(Class<? extends DatabaseObject> type) {
this.type = type;
Expand Down

0 comments on commit 2e87acd

Please sign in to comment.