diff --git a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy
index f660927c23c..115127ba8e1 100644
--- a/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy
+++ b/liquibase-integration-tests/src/test/resources/liquibase/extension/testing/command/calculateChecksum.test.groovy
@@ -11,10 +11,17 @@ Short Description: Calculates and prints a checksum for the changeset
Long Description: Calculates and prints a checksum for the changeset with the given id in the format filepath::id::author
Required Args:
changelogFile (String) The root changelog file
- changesetIdentifier (String) Changeset ID identifier of form filepath::id::author
url (String) The JDBC database connection URL
OBFUSCATED
Optional Args:
+ changesetAuthor (String) ChangeSet Author attribute
+ Default: null
+ changesetId (String) ChangeSet ID attribute
+ Default: null
+ changesetIdentifier (String) ChangeSet identifier of form filepath::id::author
+ Default: null
+ changesetPath (String) Changelog path in which the changeSet is included
+ Default: null
defaultCatalogName (String) The default catalog name to use for the database connection
Default: null
defaultSchemaName (String) The default schema name to use for the database connection
@@ -30,7 +37,7 @@ Optional Args:
Default: null
"""
- run "Happy path", {
+ run "Happy path using changeSetIdentifier", {
arguments = [
url : { it.altUrl },
username : { it.altUsername },
@@ -44,22 +51,29 @@ Optional Args:
]
}
- run "Run without changelogFile should throw an exception", {
+ run "Happy path using changeSetPath, ChangeSetId and ChangeSetPath", {
arguments = [
- changesetIdentifier: "changelogs/h2/complete/rollback.tag.changelog.xml::1::nvoxland",
+ url : { it.altUrl },
+ username : { it.altUsername },
+ password : { it.altPassword },
+ changesetPath : "changelogs/h2/complete/rollback.tag.changelog.xml",
+ changesetId : "1",
+ changesetAuthor : "nvoxland",
+ changelogFile : "changelogs/h2/complete/rollback.tag.changelog.xml"
]
- expectedException = CommandValidationException.class
- expectedExceptionMessage = 'Invalid argument \'changelogFile\': missing required argument'
+ expectedResults = [
+ checksumResult : "9:10de8cd690aed1d88d837cbe555d1684"
+ ]
}
- run "Run without changesetIdentifier should throw an exception", {
+ run "Run without changelogFile should throw an exception", {
arguments = [
- changelogFile : "changelogs/h2/complete/rollback.tag.changelog.xml"
+ changesetIdentifier: "changelogs/h2/complete/rollback.tag.changelog.xml::1::nvoxland",
]
expectedException = CommandValidationException.class
- expectedExceptionMessage = "Invalid argument \'changesetIdentifier\': missing required argument"
+ expectedExceptionMessage = 'Invalid argument \'changelogFile\': missing required argument'
}
run "Run without URL should throw an exception", {
diff --git a/liquibase-standard/src/main/java/liquibase/Liquibase.java b/liquibase-standard/src/main/java/liquibase/Liquibase.java
index 00fa7e326d4..aa9816f48fe 100644
--- a/liquibase-standard/src/main/java/liquibase/Liquibase.java
+++ b/liquibase-standard/src/main/java/liquibase/Liquibase.java
@@ -1230,23 +1230,25 @@ public void clearCheckSums() throws LiquibaseException {
*/
@Deprecated
public final CheckSum calculateCheckSum(final String changeSetIdentifier) throws LiquibaseException {
- CommandResults commandResults = new CommandScope("calculateChecksum")
- .addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database)
- .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_IDENTIFIER_ARG, changeSetIdentifier)
- .addArgumentValue(CalculateChecksumCommandStep.CHANGELOG_FILE_ARG, this.changeLogFile)
- .execute();
- return commandResults.getResult(CalculateChecksumCommandStep.CHECKSUM_RESULT);
+ String changeSetAttributes[] = changeSetIdentifier.split("::");
+ //validate changeSet parameters and return an error or removed/ignore any other '::' occurrence when processing either a path, id or author.
+ return this.calculateCheckSum(changeSetAttributes[0], changeSetAttributes[1], changeSetAttributes[2]);
}
/**
- * Calculates the checksum for the values that form a given identifier
- *
- * @deprecated Use {link {@link CommandScope(String)}.
+ * Calculate the checksum for a given changeset specified by path, changeset id and author
*/
- @Deprecated
- public CheckSum calculateCheckSum(final String filename, final String id, final String author)
+ public CheckSum calculateCheckSum(final String changeSetPath, final String changeSetId, final String changeSetAuthor)
throws LiquibaseException {
- return this.calculateCheckSum(String.format("%s::%s::%s", filename, id, author));
+ CommandResults commandResults = new CommandScope("calculateChecksum")
+ .addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database)
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_PATH_ARG, changeSetPath)
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_ID_ARG, changeSetId)
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_AUTHOR_ARG, changeSetAuthor)
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_IDENTIFIER_ARG, String.format("%s::%s::%s", changeSetPath, changeSetId, changeSetAuthor))
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGELOG_FILE_ARG, this.changeLogFile)
+ .execute();
+ return commandResults.getResult(CalculateChecksumCommandStep.CHECKSUM_RESULT);
}
@Deprecated
diff --git a/liquibase-standard/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java b/liquibase-standard/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java
index 8c61eb46c1f..7ae685a2373 100644
--- a/liquibase-standard/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java
+++ b/liquibase-standard/src/main/java/liquibase/command/core/CalculateChecksumCommandStep.java
@@ -3,11 +3,22 @@
import liquibase.ChecksumVersion;
import liquibase.Scope;
import liquibase.change.CheckSum;
-import liquibase.changelog.*;
-import liquibase.command.*;
+import liquibase.changelog.ChangeLogHistoryService;
+import liquibase.changelog.ChangeLogHistoryServiceFactory;
+import liquibase.changelog.ChangeLogParameters;
+import liquibase.changelog.ChangeSet;
+import liquibase.changelog.DatabaseChangeLog;
+import liquibase.changelog.RanChangeSet;
+import liquibase.command.AbstractCommandStep;
+import liquibase.command.CommandArgumentDefinition;
+import liquibase.command.CommandBuilder;
+import liquibase.command.CommandDefinition;
+import liquibase.command.CommandResultDefinition;
+import liquibase.command.CommandResultsBuilder;
+import liquibase.command.CommandScope;
+import liquibase.command.CommonArgumentNames;
import liquibase.database.Database;
import liquibase.exception.LiquibaseException;
-import liquibase.integration.commandline.LiquibaseCommandLineConfiguration;
import liquibase.parser.ChangeLogParserFactory;
import liquibase.resource.ResourceAccessor;
import liquibase.util.StringUtil;
@@ -20,22 +31,41 @@ public class CalculateChecksumCommandStep extends AbstractCommandStep {
protected static final String[] COMMAND_NAME = {"calculateChecksum"};
public static final CommandArgumentDefinition CHANGELOG_FILE_ARG;
- public static final CommandArgumentDefinition CHANGESET_IDENTIFIER_ARG;
+ public static final CommandArgumentDefinition CHANGESET_PATH_ARG;
+
+ public static final CommandArgumentDefinition CHANGESET_ID_ARG;
+
+ public static final CommandArgumentDefinition CHANGESET_AUTHOR_ARG;
public static final CommandResultDefinition CHECKSUM_RESULT;
- protected static final int CHANGESET_ID_NUM_PARTS = 3;
- protected static final int CHANGESET_ID_AUTHOR_PART = 2;
- protected static final int CHANGESET_ID_CHANGESET_PART = 1;
- protected static final int CHANGESET_ID_CHANGELOG_PART = 0;
+ public static final CommandArgumentDefinition CHANGESET_IDENTIFIER_ARG;
+ private static final int CHANGESET_IDENTIFIER_PARTS_LENGTH = 3;
+ private static final int CHANGESET_IDENTIFIER_AUTHOR_PART = 2;
+ private static final int CHANGESET_IDENTIFIER_ID_PART = 1;
+ private static final int CHANGESET_IDENTIFIER_PATH_PART = 0;
static {
CommandBuilder builder = new CommandBuilder(COMMAND_NAME);
CHANGELOG_FILE_ARG = builder.argument(CommonArgumentNames.CHANGELOG_FILE, String.class).required()
- .description("The root changelog file").build();
- CHANGESET_IDENTIFIER_ARG = builder.argument("changesetIdentifier", String.class).required()
- .description("Changeset ID identifier of form filepath::id::author").build();
+ .description("The root changelog file").build();
+
+ CHANGESET_IDENTIFIER_ARG = builder.argument("changesetIdentifier", String.class)
+ .description("ChangeSet identifier of form filepath::id::author")
+ .build();
+
+ CHANGESET_PATH_ARG = builder.argument("changesetPath", String.class)
+ .description("Changelog path in which the changeSet is included")
+ .build();
+
+ CHANGESET_ID_ARG = builder.argument("changesetId", String.class)
+ .description("ChangeSet ID attribute")
+ .build();
+
+ CHANGESET_AUTHOR_ARG = builder.argument("changesetAuthor", String.class)
+ .description("ChangeSet Author attribute")
+ .build();
CHECKSUM_RESULT = builder.result("checksumResult", CheckSum.class).description("Calculated checksum").build();
}
@@ -47,42 +77,102 @@ public List> requiredDependencies() {
@Override
public String[][] defineCommandNames() {
- return new String[][] { COMMAND_NAME };
+ return new String[][]{COMMAND_NAME};
}
@Override
public void run(CommandResultsBuilder resultsBuilder) throws Exception {
CommandScope commandScope = resultsBuilder.getCommandScope();
+
final String changeSetIdentifier = commandScope.getArgumentValue(CHANGESET_IDENTIFIER_ARG);
final String changeLogFile = commandScope.getArgumentValue(CHANGELOG_FILE_ARG).replace('\\', '/');
+ String changeSetPath;
+ String changeSetId;
+ String changeSetAuthor;
+
+ final boolean isChangeSetIdentifierPassed = changeSetIdentifier != null;
+
+ validateIdentifierParameters(commandScope, changeSetIdentifier);
+
+ if (isChangeSetIdentifierPassed) {
+ List parts = validateAndExtractParts(changeSetIdentifier, changeLogFile);
+ changeSetPath = parts.get(CHANGESET_IDENTIFIER_PATH_PART);
+ changeSetId = parts.get(CHANGESET_IDENTIFIER_ID_PART);
+ changeSetAuthor = parts.get(CHANGESET_IDENTIFIER_AUTHOR_PART);
+ } else {
+ changeSetPath = commandScope.getArgumentValue(CHANGESET_PATH_ARG);
+ changeSetId = commandScope.getArgumentValue(CHANGESET_ID_ARG);
+ changeSetAuthor = commandScope.getArgumentValue(CHANGESET_AUTHOR_ARG);
+ }
+
final Database database = (Database) commandScope.getDependency(Database.class);
- List parts = validateAndExtractParts(changeSetIdentifier, changeLogFile);
- Scope.getCurrentScope().getLog(getClass()).info(String.format("Calculating checksum for changeset %s", changeSetIdentifier));
+ Scope.getCurrentScope().getLog(getClass()).info(String.format("Calculating checksum for changeSet identified by changeSet id: %s, author: %s, path: %s",
+ changeSetId,
+ changeSetAuthor,
+ changeSetPath
+ ));
ResourceAccessor resourceAccessor = Scope.getCurrentScope().getResourceAccessor();
DatabaseChangeLog changeLog = ChangeLogParserFactory.getInstance().getParser(
- changeLogFile, resourceAccessor).parse(changeLogFile, new ChangeLogParameters(database), resourceAccessor);
+ changeLogFile, resourceAccessor).parse(changeLogFile, new ChangeLogParameters(database), resourceAccessor);
- ChangeSet changeSet = changeLog.getChangeSet(parts.get(CHANGESET_ID_CHANGELOG_PART), parts.get(CHANGESET_ID_AUTHOR_PART),
- parts.get(CHANGESET_ID_CHANGESET_PART));
+ ChangeSet changeSet = changeLog.getChangeSet(changeSetPath, changeSetAuthor, changeSetId);
if (changeSet == null) {
- throw new LiquibaseException(new IllegalArgumentException("No such changeSet: " + changeSetIdentifier));
+ throw new LiquibaseException(new IllegalArgumentException(String.format("No such changeSet identified by changeSet id: %s, author: %s, path: %s",
+ changeSetId,
+ changeSetAuthor,
+ changeSetPath
+ )));
}
ChangeLogHistoryService changeLogService = ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database);
RanChangeSet ranChangeSet = changeLogService.getRanChangeSet(changeSet);
sendMessages(resultsBuilder, changeSet.generateCheckSum(
- ranChangeSet != null && ranChangeSet.getLastCheckSum() != null ?
- ChecksumVersion.enumFromChecksumVersion(ranChangeSet.getLastCheckSum().getVersion()) : ChecksumVersion.latest()
- )
+ ranChangeSet != null && ranChangeSet.getLastCheckSum() != null ?
+ ChecksumVersion.enumFromChecksumVersion(ranChangeSet.getLastCheckSum().getVersion()) : ChecksumVersion.latest()
+ )
);
}
- private static void sendMessages(CommandResultsBuilder resultsBuilder, CheckSum checkSum) {
- resultsBuilder.addResult(CHECKSUM_RESULT, checkSum);
- Scope.getCurrentScope().getUI().sendMessage(checkSum.toString());
+ private void validateIdentifierParameters(CommandScope commandScope, String changeSetIdentifier) throws LiquibaseException {
+ final boolean isAmbiguousNumberOfIdentifierProvided = (commandScope.getArgumentValue(CHANGESET_ID_ARG) != null ||
+ commandScope.getArgumentValue(CHANGESET_AUTHOR_ARG) != null || commandScope.getArgumentValue(CHANGESET_PATH_ARG) != null)
+ && changeSetIdentifier != null;
+
+ if (isAmbiguousNumberOfIdentifierProvided) {
+ String errorMessage = "Error encountered while parsing the command line. " +
+ "'--changeset-identifier' cannot be provided alongside other changeset arguments: " +
+ "'--changeset-id', '--changeset-path', '--changeset-author'.";
+ throw new LiquibaseException(new IllegalArgumentException(errorMessage));
+ }
+
+ final boolean isRequiredCompositeIdentifierMissing = (commandScope.getArgumentValue(CHANGESET_ID_ARG) == null ||
+ commandScope.getArgumentValue(CHANGESET_AUTHOR_ARG) == null || commandScope.getArgumentValue(CHANGESET_PATH_ARG) == null)
+ && changeSetIdentifier == null;
+
+ if (isRequiredCompositeIdentifierMissing) {
+ String errorMessage = "Error encountered while parsing the command line. " +
+ "If --changeset-identifier is not provided than --changeset-id, --changeset-author and --changeset-path must be specified. " +
+ "Missing argument: ";
+
+ if (commandScope.getArgumentValue(CHANGESET_ID_ARG) == null) {
+ errorMessage = errorMessage + " '--changeset-id',";
+ }
+
+ if (commandScope.getArgumentValue(CHANGESET_AUTHOR_ARG) == null) {
+ errorMessage = errorMessage + " '--changeset-author',";
+ }
+
+ if (commandScope.getArgumentValue(CHANGESET_PATH_ARG) == null) {
+ errorMessage = errorMessage + " '--changeset-path',";
+ }
+
+ errorMessage = errorMessage.substring(0,errorMessage.length() - 1) + ".";
+
+ throw new LiquibaseException(new IllegalArgumentException(errorMessage));
+ }
}
private List validateAndExtractParts(String changeSetIdentifier, String changeLogFile) throws LiquibaseException {
@@ -95,7 +185,7 @@ private List validateAndExtractParts(String changeSetIdentifier, String
}
final List parts = StringUtil.splitAndTrim(changeSetIdentifier, "::");
- if ((parts == null) || (parts.size() < CHANGESET_ID_NUM_PARTS)) {
+ if ((parts == null) || (parts.size() < CHANGESET_IDENTIFIER_PARTS_LENGTH)) {
throw new LiquibaseException(
new IllegalArgumentException("Invalid changeSet identifier: " + changeSetIdentifier)
);
@@ -103,9 +193,24 @@ private List validateAndExtractParts(String changeSetIdentifier, String
return parts;
}
+ private static void sendMessages(CommandResultsBuilder resultsBuilder, CheckSum checkSum) {
+ resultsBuilder.addResult(CHECKSUM_RESULT, checkSum);
+ Scope.getCurrentScope().getUI().sendMessage(checkSum.toString());
+ }
+
@Override
public void adjustCommandDefinition(CommandDefinition commandDefinition) {
commandDefinition.setShortDescription("Calculates and prints a checksum for the changeset");
- commandDefinition.setLongDescription("Calculates and prints a checksum for the changeset with the given id in the format filepath::id::author");
+ commandDefinition.setLongDescription(
+ "Calculates and prints a checksum for the changeset with the given id in the format filepath::id::author");
+ commandDefinition.setHelpFooter("\nCalculate checksum provides two ways to identify a changeSet.\n\n" +
+ "1. Composite changeSet identifier\n\n" +
+ "The composite changeSet identifier must be passed in the following pattern myPath::myId::myAuthor.\n\n" +
+ "liquibase calculateCheckSum --changesetIdentifier myFile::myId::myAuthor\n\n" +
+ "2. Individual changeSet parameters\n\n" +
+ "The second option requires all three parameters to be defined.\n" +
+ "This variant offers some more flexibility in naming conventions for path, id and author.\n\n"+
+ "liquibase calculateCheckSum --changesetId myId --changesetAuthor myAuthor --changesetPath myPath\n"
+ );
}
}
diff --git a/liquibase-standard/src/main/java/liquibase/integration/commandline/Main.java b/liquibase-standard/src/main/java/liquibase/integration/commandline/Main.java
index 27d4c6dacb4..d96b2d103d7 100644
--- a/liquibase-standard/src/main/java/liquibase/integration/commandline/Main.java
+++ b/liquibase-standard/src/main/java/liquibase/integration/commandline/Main.java
@@ -2,7 +2,6 @@
import liquibase.*;
import liquibase.changelog.ChangeLogParameters;
-import liquibase.changelog.DatabaseChangeLog;
import liquibase.changelog.visitor.ChangeExecListener;
import liquibase.changelog.visitor.DefaultChangeExecListener;
import liquibase.command.CommandResults;
@@ -974,19 +973,7 @@ private void checkForMalformedCommandParameters(final List messages) {
return;
}
- final int CHANGESET_MINIMUM_IDENTIFIER_PARTS = 3;
-
- if (COMMANDS.CALCULATE_CHECKSUM.equalsIgnoreCase(command)) {
- for (final String param : commandParams) {
- if ((param != null) && !param.startsWith("-")) {
- final String[] parts = param.split("::");
- if (parts.length < CHANGESET_MINIMUM_IDENTIFIER_PARTS) {
- messages.add(coreBundle.getString("changeset.identifier.must.have.form.filepath.id.author"));
- break;
- }
- }
- }
- } else if (COMMANDS.DIFF_CHANGELOG.equalsIgnoreCase(command) && (diffTypes != null) && diffTypes.toLowerCase
+ if (COMMANDS.DIFF_CHANGELOG.equalsIgnoreCase(command) && (diffTypes != null) && diffTypes.toLowerCase
().contains("data")) {
messages.add(String.format(coreBundle.getString("including.data.diffchangelog.has.no.effect"),
OPTIONS.DIFF_TYPES, COMMANDS.GENERATE_CHANGELOG
@@ -1514,7 +1501,17 @@ protected void doMigration() throws Exception {
liquibase.clearCheckSums();
return;
} else if (COMMANDS.CALCULATE_CHECKSUM.equalsIgnoreCase(command)) {
- liquibase.calculateCheckSum(commandParams.iterator().next());
+ CommandScope calculateChecksumCommand = new CommandScope("calculateChecksum");
+
+ calculateChecksumCommand
+ .addArgumentValue(DbUrlConnectionCommandStep.DATABASE_ARG, database)
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_PATH_ARG, getCommandParam(OPTIONS.CHANGE_SET_PATH, null))
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_ID_ARG, getCommandParam(OPTIONS.CHANGE_SET_ID, null))
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_AUTHOR_ARG, getCommandParam(OPTIONS.CHANGE_SET_AUTHOR, null))
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGESET_IDENTIFIER_ARG, getCommandParam(OPTIONS.CHANGE_SET_IDENTIFIER, null))
+ .addArgumentValue(CalculateChecksumCommandStep.CHANGELOG_FILE_ARG, this.changeLogFile);
+
+ calculateChecksumCommand.execute();
return;
} else if (COMMANDS.DB_DOC.equalsIgnoreCase(command)) {
if (commandParams.isEmpty()) {
@@ -2186,6 +2183,8 @@ private enum OPTIONS {
private static final String CHANGELOG_FILE = "changeLogFile";
private static final String DATA_OUTPUT_DIRECTORY = "dataOutputDirectory";
private static final String DIFF_TYPES = "diffTypes";
+
+ public static final String CHANGE_SET_IDENTIFIER = "changeSetIdentifier";
private static final String CHANGE_SET_ID = "changeSetId";
private static final String CHANGE_SET_AUTHOR = "changeSetAuthor";
private static final String CHANGE_SET_PATH = "changeSetPath";