liquibase.Liquibase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of liquibase-core Show documentation
Show all versions of liquibase-core Show documentation
Liquibase is a tool for managing and executing database changes.
package liquibase;
import liquibase.change.CheckSum;
import liquibase.change.core.RawSQLChange;
import liquibase.changelog.*;
import liquibase.changelog.filter.*;
import liquibase.changelog.visitor.*;
import liquibase.command.CommandScope;
import liquibase.command.core.InternalDropAllCommandStep;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.ObjectQuotingStrategy;
import liquibase.database.core.MSSQLDatabase;
import liquibase.diff.DiffGeneratorFactory;
import liquibase.diff.DiffResult;
import liquibase.diff.compare.CompareControl;
import liquibase.diff.output.changelog.DiffToChangeLog;
import liquibase.exception.*;
import liquibase.executor.Executor;
import liquibase.executor.ExecutorService;
import liquibase.executor.LoggingExecutor;
import liquibase.hub.*;
import liquibase.hub.listener.HubChangeExecListener;
import liquibase.hub.model.Connection;
import liquibase.hub.model.HubChangeLog;
import liquibase.hub.model.Operation;
import liquibase.lockservice.DatabaseChangeLogLock;
import liquibase.lockservice.LockService;
import liquibase.lockservice.LockServiceFactory;
import liquibase.logging.Logger;
import liquibase.logging.core.BufferedLogService;
import liquibase.logging.core.CompositeLogService;
import liquibase.parser.ChangeLogParser;
import liquibase.parser.ChangeLogParserFactory;
import liquibase.resource.InputStreamList;
import liquibase.resource.ResourceAccessor;
import liquibase.serializer.ChangeLogSerializer;
import liquibase.snapshot.DatabaseSnapshot;
import liquibase.snapshot.InvalidExampleException;
import liquibase.snapshot.SnapshotControl;
import liquibase.snapshot.SnapshotGeneratorFactory;
import liquibase.statement.core.RawSqlStatement;
import liquibase.statement.core.UpdateStatement;
import liquibase.structure.DatabaseObject;
import liquibase.structure.core.Catalog;
import liquibase.util.LiquibaseUtil;
import liquibase.util.StreamUtil;
import liquibase.util.StringUtil;
import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Writer;
import java.text.DateFormat;
import java.util.*;
import java.util.function.Supplier;
import static java.util.ResourceBundle.getBundle;
/**
* Primary facade class for interacting with Liquibase.
* The built in command line, Ant, Maven and other ways of running Liquibase are wrappers around methods in this class.
*/
public class Liquibase implements AutoCloseable {
private static final Logger LOG = Scope.getCurrentScope().getLog(Liquibase.class);
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;
private static final ResourceBundle coreBundle = getBundle("liquibase/i18n/liquibase-core");
public static final String MSG_COULD_NOT_RELEASE_LOCK = coreBundle.getString("could.not.release.lock");
protected Database database;
private DatabaseChangeLog databaseChangeLog;
private String changeLogFile;
private final ResourceAccessor resourceAccessor;
private final ChangeLogParameters changeLogParameters;
private ChangeExecListener changeExecListener;
private ChangeLogSyncListener changeLogSyncListener;
private UUID hubConnectionId;
private enum RollbackMessageType {
WILL_ROLLBACK, ROLLED_BACK, ROLLBACK_FAILED
}
/**
* Creates a Liquibase instance for a given DatabaseConnection. The Database instance used will be found with {@link DatabaseFactory#findCorrectDatabaseImplementation(liquibase.database.DatabaseConnection)}
*
* @see DatabaseConnection
* @see Database
* @see #Liquibase(String, liquibase.resource.ResourceAccessor, liquibase.database.Database)
* @see ResourceAccessor
*/
public Liquibase(String changeLogFile, ResourceAccessor resourceAccessor, DatabaseConnection conn)
throws LiquibaseException {
this(changeLogFile, resourceAccessor, DatabaseFactory.getInstance().findCorrectDatabaseImplementation(conn));
}
/**
* Creates a Liquibase instance. The changeLogFile parameter must be a path that can be resolved by the passed
* ResourceAccessor. If windows style path separators are used for the changeLogFile, they will be standardized to
* unix style for better cross-system compatibility.
*
* @see DatabaseConnection
* @see Database
* @see ResourceAccessor
*/
public Liquibase(String changeLogFile, ResourceAccessor resourceAccessor, Database database) {
if (changeLogFile != null) {
// Convert to STANDARD / if using absolute path on windows:
this.changeLogFile = changeLogFile.replace('\\', '/');
}
this.resourceAccessor = resourceAccessor;
this.changeLogParameters = new ChangeLogParameters(database);
this.database = database;
}
public Liquibase(DatabaseChangeLog changeLog, ResourceAccessor resourceAccessor, Database database) {
this.databaseChangeLog = changeLog;
if (changeLog != null) {
this.changeLogFile = changeLog.getPhysicalFilePath();
}
if (this.changeLogFile != null) {
// Convert to STANDARD "/" if using an absolute path on Windows:
changeLogFile = changeLogFile.replace('\\', '/');
}
this.resourceAccessor = resourceAccessor;
this.database = database;
this.changeLogParameters = new ChangeLogParameters(database);
}
public UUID getHubConnectionId() {
return hubConnectionId;
}
public void setHubConnectionId(UUID hubConnectionId) {
this.hubConnectionId = hubConnectionId;
}
/**
* Return the change log file used by this Liquibase instance.
*/
public String getChangeLogFile() {
return changeLogFile;
}
/**
* Return the log used by this Liquibase instance.
*/
public Logger getLog() {
return LOG;
}
/**
* Returns the ChangeLogParameters container used by this Liquibase instance.
*/
public ChangeLogParameters getChangeLogParameters() {
return changeLogParameters;
}
/**
* Returns the Database used by this Liquibase instance.
*/
public Database getDatabase() {
return database;
}
/**
* Return ResourceAccessor used by this Liquibase instance.
*/
public ResourceAccessor getResourceAccessor() {
return resourceAccessor;
}
/**
* Convience method for {@link #update(Contexts)} that constructs the Context object from the passed string.
*/
public void update(String contexts) throws LiquibaseException {
this.update(new Contexts(contexts));
}
/**
* Executes Liquibase "update" logic which ensures that the configured {@link Database} is up to date according to
* the configured changelog file. To run in "no context mode", pass a null or empty context object.
*/
public void update(Contexts contexts) throws LiquibaseException {
update(contexts, new LabelExpression());
}
public void update(Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
update(contexts, labelExpression, true);
}
/**
*
* Liquibase update
*
* @param contexts
* @param labelExpression
* @param checkLiquibaseTables
* @throws LiquibaseException
*
*/
public void update(Contexts contexts, LabelExpression labelExpression, boolean checkLiquibaseTables) throws LiquibaseException {
runInScope(() -> {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
Operation updateOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
if (checkLiquibaseTables) {
checkLiquibaseTables(true, changeLog, contexts, labelExpression);
}
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(new Date(), changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create or retrieve the Connection if this is not SQL generation
// Make sure the Hub is available here by checking the return
// We do not need a connection if we are using a LoggingExecutor
//
ChangeLogIterator changeLogIterator = getStandardChangelogIterator(contexts, labelExpression, changeLog);
Connection connection = getConnection(changeLog);
if (connection != null) {
updateOperation =
hubUpdater.preUpdateHub("UPDATE", "update", connection, changeLogFile, contexts, labelExpression, changeLogIterator);
}
//
// Make sure we don't already have a listener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(updateOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator runChangeLogIterator = getStandardChangelogIterator(contexts, labelExpression, changeLog);
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
runChangeLogIterator.run(createUpdateVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
//
// Update Hub with the operation information
//
hubUpdater.postUpdateHub(updateOperation, bufferLog);
} catch (Throwable e) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(updateOperation, bufferLog, e.getMessage());
}
throw e;
} finally {
database.setObjectQuotingStrategy(ObjectQuotingStrategy.LEGACY);
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
setChangeExecListener(null);
}
});
}
/**
*
* Create or retrieve the Connection object
*
* @param changeLog Database changelog
* @return Connection
* @throws LiquibaseHubException Thrown by HubService
*
*/
public Connection getConnection(DatabaseChangeLog changeLog) throws LiquibaseHubException {
//
// If our current Executor is a LoggingExecutor then just return since we will not update Hub
//
Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database);
if (executor instanceof LoggingExecutor) {
return null;
}
String changeLogId = changeLog.getChangeLogId();
HubUpdater hubUpdater = new HubUpdater(new Date(), changeLog, database);
if (hubUpdater.hubIsNotAvailable(changeLogId)) {
if (StringUtil.isNotEmpty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue()) && changeLogId == null) {
String message =
"The API key '" + HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue() + "' was found, but no changelog ID exists.\n" +
"No operations will be reported. Register this changelog with Liquibase Hub to generate free deployment reports.\n" +
"Learn more at https://hub.liquibase.com.";
Scope.getCurrentScope().getUI().sendMessage("WARNING: " + message);
Scope.getCurrentScope().getLog(getClass()).warning(message);
}
return null;
}
//
// Warn about the situation where there is a changeLog ID, but no API key
//
if (StringUtil.isEmpty(HubConfiguration.LIQUIBASE_HUB_API_KEY.getCurrentValue()) && changeLogId != null) {
String message = "The changelog ID '" + changeLogId + "' was found, but no API Key exists.\n" +
"No operations will be reported. Simply add a liquibase.hub.apiKey setting to generate free deployment reports.\n" +
"Learn more at https://hub.liquibase.com.";
Scope.getCurrentScope().getUI().sendMessage("WARNING: " + message);
Scope.getCurrentScope().getLog(getClass()).warning(message);
return null;
}
Connection connection;
final HubService hubService = Scope.getCurrentScope().getSingleton(HubServiceFactory.class).getService();
if (getHubConnectionId() == null) {
HubChangeLog hubChangeLog = hubService.getHubChangeLog(UUID.fromString(changeLogId), "*");
if (hubChangeLog == null) {
Scope.getCurrentScope().getLog(getClass()).warning(
"Retrieving Hub Change Log failed for Changelog ID: " + changeLogId);
return null;
}
if (hubChangeLog.isDeleted()) {
//
// Complain and stop the operation
//
String message =
"\n" +
"The operation did not complete and will not be reported to Hub because the\n" + "" +
"registered changelog has been deleted by someone in your organization.\n" +
"Learn more at http://hub.liquibase.com.";
throw new LiquibaseHubException(message);
}
Connection exampleConnection = new Connection();
exampleConnection.setProject(hubChangeLog.getProject());
exampleConnection.setJdbcUrl(Liquibase.this.database.getConnection().getURL());
connection = hubService.getConnection(exampleConnection, true);
setHubConnectionId(connection.getId());
} else {
connection = hubService.getConnection(new Connection().setId(getHubConnectionId()), true);
}
return connection;
}
public DatabaseChangeLog getDatabaseChangeLog() throws LiquibaseException {
if (databaseChangeLog == null && changeLogFile != null) {
ChangeLogParser parser = ChangeLogParserFactory.getInstance().getParser(changeLogFile, resourceAccessor);
databaseChangeLog = parser.parse(changeLogFile, changeLogParameters, resourceAccessor);
}
return databaseChangeLog;
}
protected UpdateVisitor createUpdateVisitor() {
return new UpdateVisitor(database, changeExecListener);
}
protected RollbackVisitor createRollbackVisitor() {
return new RollbackVisitor(database, changeExecListener);
}
protected ChangeLogIterator getStandardChangelogIterator(Contexts contexts, LabelExpression labelExpression,
DatabaseChangeLog changeLog) throws DatabaseException {
return new ChangeLogIterator(changeLog,
new ShouldRunChangeSetFilter(database),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter());
}
protected ChangeLogIterator buildChangeLogIterator(String tag, DatabaseChangeLog changeLog, Contexts contexts,
LabelExpression labelExpression) throws DatabaseException {
if (tag == null) {
return new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
} else {
List ranChangeSetList = database.getRanChangeSetList();
return new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database),
new UpToTagChangeSetFilter(tag, ranChangeSetList));
}
}
public void update(String contexts, Writer output) throws LiquibaseException {
this.update(new Contexts(contexts), output);
}
public void update(Contexts contexts, Writer output) throws LiquibaseException {
update(contexts, new LabelExpression(), output);
}
public void update(Contexts contexts, LabelExpression labelExpression, Writer output) throws LiquibaseException {
update(contexts, labelExpression, output, true);
}
public void update(Contexts contexts, LabelExpression labelExpression, Writer output, boolean checkLiquibaseTables)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Update Database Script");
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
update(contexts, labelExpression, checkLiquibaseTables);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
}
});
}
public void update(int changesToApply, String contexts) throws LiquibaseException {
update(changesToApply, new Contexts(contexts), new LabelExpression());
}
/**
*
* Update to count
*
* @param changesToApply
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*
*/
public void update(int changesToApply, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
Operation updateOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(true, changeLog, contexts, labelExpression);
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(new Date(), changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
ChangeLogIterator listLogIterator = new ChangeLogIterator(changeLog,
new ShouldRunChangeSetFilter(database),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(changesToApply));
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
updateOperation =
hubUpdater.preUpdateHub("UPDATE", "update-count", connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// If we are doing Hub then set up a HubChangeExecListener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(updateOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator runChangeLogIterator = new ChangeLogIterator(changeLog,
new ShouldRunChangeSetFilter(database),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(changesToApply));
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
runChangeLogIterator.run(createUpdateVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
hubUpdater.postUpdateHub(updateOperation, bufferLog);
}
catch (Throwable e) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(updateOperation, bufferLog, e.getMessage());
}
throw e;
} finally {
database.setObjectQuotingStrategy(ObjectQuotingStrategy.LEGACY);
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
setChangeExecListener(null);
}
}
});
}
public void update(String tag, String contexts) throws LiquibaseException {
update(tag, new Contexts(contexts), new LabelExpression());
}
public void update(String tag, Contexts contexts) throws LiquibaseException {
update(tag, contexts, new LabelExpression());
}
/**
*
* Update to tag
*
* @param tag Tag to update for
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*
*/
public void update(String tag, Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
if (tag == null) {
update(contexts, labelExpression);
return;
}
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
HubUpdater hubUpdater = null;
Operation updateOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(true, changeLog, contexts, labelExpression);
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(new Date(), changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
List ranChangeSetList = database.getRanChangeSetList();
ChangeLogIterator listLogIterator = new ChangeLogIterator(changeLog,
new ShouldRunChangeSetFilter(database),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new UpToTagChangeSetFilter(tag, ranChangeSetList));
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
updateOperation =
hubUpdater.preUpdateHub("UPDATE", "update-to-tag", connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// Check for an already existing Listener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(updateOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator runChangeLogIterator = new ChangeLogIterator(changeLog,
new ShouldRunChangeSetFilter(database),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new UpToTagChangeSetFilter(tag, ranChangeSetList));
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
runChangeLogIterator.run(createUpdateVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
hubUpdater.postUpdateHub(updateOperation, bufferLog);
}
catch (Throwable e) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(updateOperation, bufferLog, e.getMessage());
}
throw e;
} finally {
database.setObjectQuotingStrategy(ObjectQuotingStrategy.LEGACY);
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
setChangeExecListener(null);
}
}
});
}
public void update(int changesToApply, String contexts, Writer output) throws LiquibaseException {
this.update(changesToApply, new Contexts(contexts), new LabelExpression(), output);
}
public void update(int changesToApply, Contexts contexts, LabelExpression labelExpression, Writer output) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Update " + changesToApply + " Change Sets Database Script");
update(changesToApply, contexts, labelExpression);
flushOutputWriter(output);
resetServices();
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
}
});
}
public void update(String tag, String contexts, Writer output) throws LiquibaseException {
update(tag, new Contexts(contexts), new LabelExpression(), output);
}
public void update(String tag, Contexts contexts, Writer output) throws LiquibaseException {
update(tag, contexts, new LabelExpression(), output);
}
public void update(String tag, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
if (tag == null) {
update(contexts, labelExpression, output);
return;
}
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Update to '" + tag + "' Database Script");
update(tag, contexts, labelExpression);
flushOutputWriter(output);
resetServices();
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
}
});
}
public void outputHeader(String message) throws DatabaseException {
Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("logging", database);
executor.comment("*********************************************************************");
executor.comment(message);
executor.comment("*********************************************************************");
executor.comment("Change Log: " + changeLogFile);
executor.comment("Ran at: " +
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT).format(new Date())
);
DatabaseConnection connection = getDatabase().getConnection();
if (connection != null) {
executor.comment("Against: " + connection.getConnectionUserName() + "@" + connection.getURL());
}
executor.comment("Liquibase version: " + LiquibaseUtil.getBuildVersionInfo());
executor.comment("*********************************************************************" +
StreamUtil.getLineSeparator()
);
if ((database instanceof MSSQLDatabase) && (database.getDefaultCatalogName() != null)) {
executor.execute(new RawSqlStatement("USE " +
database.escapeObjectName(database.getDefaultCatalogName(), Catalog.class) + ";")
);
}
}
public void rollback(int changesToRollback, String contexts, Writer output) throws LiquibaseException {
rollback(changesToRollback, null, contexts, output);
}
public void rollback(int changesToRollback, Contexts contexts, Writer output) throws LiquibaseException {
rollback(changesToRollback, null, contexts, output);
}
public void rollback(int changesToRollback, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
rollback(changesToRollback, null, contexts, labelExpression, output);
}
public void rollback(int changesToRollback, String rollbackScript, String contexts, Writer output)
throws LiquibaseException {
rollback(changesToRollback, rollbackScript, new Contexts(contexts), output);
}
public void rollback(int changesToRollback, String rollbackScript, Contexts contexts, Writer output)
throws LiquibaseException {
rollback(changesToRollback, rollbackScript, contexts, new LabelExpression(), output);
}
public void rollback(int changesToRollback, String rollbackScript, Contexts contexts,
LabelExpression labelExpression, Writer output) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Rollback " + changesToRollback + " Change(s) Script");
rollback(changesToRollback, rollbackScript, contexts, labelExpression);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
}
});
}
public void rollback(int changesToRollback, String contexts) throws LiquibaseException {
rollback(changesToRollback, null, contexts);
}
public void rollback(int changesToRollback, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
rollback(changesToRollback, null, contexts, labelExpression);
}
public void rollback(int changesToRollback, String rollbackScript, String contexts) throws LiquibaseException {
rollback(changesToRollback, rollbackScript, new Contexts(contexts), new LabelExpression());
}
/**
*
* Rollback count
*
* @param changesToRollback
* @param rollbackScript
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*/
public void rollback(int changesToRollback, String rollbackScript, Contexts contexts,
LabelExpression labelExpression) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
Operation rollbackOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
Date startTime = new Date();
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(startTime, changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
ChangeLogIterator listLogIterator = new ChangeLogIterator(database.getRanChangeSetList(), changeLog,
new AlreadyRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(changesToRollback));
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
rollbackOperation = hubUpdater.preUpdateHub("ROLLBACK", "rollback-count", connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// If we are doing Hub then set up a HubChangeExecListener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(rollbackOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator logIterator = new ChangeLogIterator(database.getRanChangeSetList(), changeLog,
new AlreadyRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(changesToRollback));
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
if (rollbackScript == null) {
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
logIterator.run(createRollbackVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
} else {
List changeSets = determineRollbacks(logIterator, contexts, labelExpression);
Map values = new HashMap<>();
values.put(Scope.Attr.logService.name(), compositeLogService);
values.put(BufferedLogService.class.getName(), bufferLog);
Scope.child(values, () -> {
executeRollbackScript(rollbackScript, changeSets, contexts, labelExpression);
});
removeRunStatus(changeSets, contexts, labelExpression);
}
hubUpdater.postUpdateHub(rollbackOperation, bufferLog);
}
catch (Throwable t) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(rollbackOperation, bufferLog, t.getMessage());
}
throw t;
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe("Error releasing lock", e);
}
resetServices();
setChangeExecListener(null);
}
}
});
}
private List determineRollbacks(ChangeLogIterator logIterator, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
List changeSetsToRollback = new ArrayList<>();
logIterator.run(new ChangeSetVisitor() {
@Override
public Direction getDirection() {
return Direction.REVERSE;
}
@Override
public void visit(ChangeSet changeSet, DatabaseChangeLog databaseChangeLog, Database database,
Set filterResults) throws LiquibaseException {
changeSetsToRollback.add(changeSet);
}
}, new RuntimeEnvironment(database, contexts, labelExpression));
return changeSetsToRollback;
}
protected void removeRunStatus(List changeSets, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
for (ChangeSet changeSet : changeSets) {
database.removeRanStatus(changeSet);
database.commit();
}
}
protected void executeRollbackScript(String rollbackScript, List changeSets, Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
final Executor executor = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database);
String rollbackScriptContents;
try (InputStreamList streams = resourceAccessor.openStreams(null, rollbackScript)) {
if ((streams == null) || streams.isEmpty()) {
throw new LiquibaseException("WARNING: The rollback script '" + rollbackScript + "' was not located. Please check your parameters. No rollback was performed");
} else if (streams.size() > 1) {
throw new LiquibaseException("Found multiple rollbackScripts named " + rollbackScript);
}
rollbackScriptContents = StreamUtil.readStreamAsString(streams.iterator().next());
} catch (IOException e) {
throw new LiquibaseException("Error reading rollbackScript " + executor + ": " + e.getMessage());
}
//
// Expand changelog properties
//
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
DatabaseChangeLog changelog = getDatabaseChangeLog();
rollbackScriptContents = changeLogParameters.expandExpressions(rollbackScriptContents, changelog);
RawSQLChange rollbackChange = buildRawSQLChange(rollbackScriptContents);
try {
((HubChangeExecListener)changeExecListener).setRollbackScriptContents(rollbackScriptContents);
sendRollbackMessages(changeSets, changelog, RollbackMessageType.WILL_ROLLBACK, contexts, labelExpression, null);
executor.execute(rollbackChange);
sendRollbackMessages(changeSets, changelog, RollbackMessageType.ROLLED_BACK, contexts, labelExpression, null);
} catch (DatabaseException e) {
Scope.getCurrentScope().getLog(getClass()).warning(e.getMessage());
LOG.severe("Error executing rollback script: " + e.getMessage());
if (changeExecListener != null) {
sendRollbackMessages(changeSets, changelog, RollbackMessageType.ROLLBACK_FAILED, contexts, labelExpression, e);
}
throw new DatabaseException("Error executing rollback script", e);
}
database.commit();
}
private void sendRollbackMessages(List changeSets,
DatabaseChangeLog changelog,
RollbackMessageType messageType,
Contexts contexts,
LabelExpression labelExpression,
Exception exception) throws LiquibaseException {
for (ChangeSet changeSet : changeSets) {
if (messageType == RollbackMessageType.WILL_ROLLBACK) {
changeExecListener.willRollback(changeSet, databaseChangeLog, database);
}
else if (messageType == RollbackMessageType.ROLLED_BACK) {
final String message = "Rolled Back Changeset:" + changeSet.toString(false);
Scope.getCurrentScope().getUI().sendMessage(message);
LOG.info(message);
changeExecListener.rolledBack(changeSet, databaseChangeLog, database);
}
else if (messageType == RollbackMessageType.ROLLBACK_FAILED) {
final String message = "Failed rolling back Changeset:" + changeSet.toString(false);
Scope.getCurrentScope().getUI().sendMessage(message);
changeExecListener.rollbackFailed(changeSet, databaseChangeLog, database, exception);
}
}
}
protected RawSQLChange buildRawSQLChange(String rollbackScriptContents) {
RawSQLChange rollbackChange = new RawSQLChange(rollbackScriptContents);
rollbackChange.setSplitStatements(true);
rollbackChange.setStripComments(true);
return rollbackChange;
}
public void rollback(String tagToRollBackTo, String contexts, Writer output) throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts, output);
}
public void rollback(String tagToRollBackTo, Contexts contexts, Writer output) throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts, output);
}
public void rollback(String tagToRollBackTo, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts, labelExpression, output);
}
public void rollback(String tagToRollBackTo, String rollbackScript, String contexts, Writer output)
throws LiquibaseException {
rollback(tagToRollBackTo, rollbackScript, new Contexts(contexts), output);
}
public void rollback(String tagToRollBackTo, String rollbackScript, Contexts contexts, Writer output)
throws LiquibaseException {
rollback(tagToRollBackTo, rollbackScript, contexts, new LabelExpression(), output);
}
public void rollback(String tagToRollBackTo, String rollbackScript, Contexts contexts,
LabelExpression labelExpression, Writer output) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Rollback to '" + tagToRollBackTo + "' Script");
rollback(tagToRollBackTo, contexts, labelExpression);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
}
public void rollback(String tagToRollBackTo, String contexts) throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts);
}
public void rollback(String tagToRollBackTo, Contexts contexts) throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts);
}
public void rollback(String tagToRollBackTo, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
rollback(tagToRollBackTo, null, contexts, labelExpression);
}
public void rollback(String tagToRollBackTo, String rollbackScript, String contexts) throws LiquibaseException {
rollback(tagToRollBackTo, rollbackScript, new Contexts(contexts));
}
public void rollback(String tagToRollBackTo, String rollbackScript, Contexts contexts) throws LiquibaseException {
rollback(tagToRollBackTo, rollbackScript, contexts, new LabelExpression());
}
/**
*
* Rollback to tag
*
* @param tagToRollBackTo
* @param rollbackScript
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*/
public void rollback(String tagToRollBackTo, String rollbackScript, Contexts contexts,
LabelExpression labelExpression) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
Operation rollbackOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
Date startTime = new Date();
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(startTime, changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
List ranChangeSetList = database.getRanChangeSetList();
ChangeLogIterator listLogIterator = new ChangeLogIterator(ranChangeSetList, changeLog,
new AfterTagChangeSetFilter(tagToRollBackTo, ranChangeSetList),
new AlreadyRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
rollbackOperation = hubUpdater.preUpdateHub("ROLLBACK", "rollback", connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// If we are doing Hub then set up a HubChangeExecListener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(rollbackOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator logIterator = new ChangeLogIterator(ranChangeSetList, changeLog,
new AfterTagChangeSetFilter(tagToRollBackTo, ranChangeSetList),
new AlreadyRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
if (rollbackScript == null) {
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
logIterator.run(createRollbackVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
} else {
List changeSets = determineRollbacks(logIterator, contexts, labelExpression);
Map values = new HashMap<>();
values.put(Scope.Attr.logService.name(), compositeLogService);
values.put(BufferedLogService.class.getName(), bufferLog);
Scope.child(values, () -> {
executeRollbackScript(rollbackScript, changeSets, contexts, labelExpression);
});
removeRunStatus(changeSets, contexts, labelExpression);
}
hubUpdater.postUpdateHub(rollbackOperation, bufferLog);
}
catch (Throwable t) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(rollbackOperation, bufferLog, t.getMessage());
}
throw t;
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
}
resetServices();
setChangeExecListener(null);
}
});
}
public void rollback(Date dateToRollBackTo, String contexts, Writer output) throws LiquibaseException {
rollback(dateToRollBackTo, null, contexts, output);
}
public void rollback(Date dateToRollBackTo, String rollbackScript, String contexts, Writer output)
throws LiquibaseException {
rollback(dateToRollBackTo, new Contexts(contexts), new LabelExpression(), output);
}
public void rollback(Date dateToRollBackTo, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
rollback(dateToRollBackTo, null, contexts, labelExpression, output);
}
public void rollback(Date dateToRollBackTo, String rollbackScript, Contexts contexts,
LabelExpression labelExpression, Writer output) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("Rollback to " + dateToRollBackTo + " Script");
rollback(dateToRollBackTo, contexts, labelExpression);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
}
private Executor getAndReplaceJdbcExecutor(Writer output) {
/* We have no other choice than to save the current Executor here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database);
final LoggingExecutor loggingExecutor = new LoggingExecutor(oldTemplate, output, database);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("logging", database, loggingExecutor);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, loggingExecutor);
return oldTemplate;
}
public void rollback(Date dateToRollBackTo, String contexts) throws LiquibaseException {
rollback(dateToRollBackTo, null, contexts);
}
public void rollback(Date dateToRollBackTo, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
rollback(dateToRollBackTo, null, contexts, labelExpression);
}
public void rollback(Date dateToRollBackTo, String rollbackScript, String contexts) throws LiquibaseException {
rollback(dateToRollBackTo, new Contexts(contexts), new LabelExpression());
}
/**
*
* Rollback to date
*
* @param dateToRollBackTo
* @param rollbackScript
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*/
public void rollback(Date dateToRollBackTo, String rollbackScript, Contexts contexts,
LabelExpression labelExpression) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
Operation rollbackOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
Date startTime = new Date();
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(startTime, changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
List ranChangeSetList = database.getRanChangeSetList();
ChangeLogIterator listLogIterator = new ChangeLogIterator(ranChangeSetList, changeLog,
new ExecutedAfterChangeSetFilter(dateToRollBackTo, ranChangeSetList),
new AlreadyRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
rollbackOperation = hubUpdater.preUpdateHub("ROLLBACK", "rollback-to-date", connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// If we are doing Hub then set up a HubChangeExecListener
//
if (connection != null) {
changeExecListener = new HubChangeExecListener(rollbackOperation, changeExecListener);
}
//
// Create another iterator to run
//
ChangeLogIterator logIterator = new ChangeLogIterator(ranChangeSetList, changeLog,
new ExecutedAfterChangeSetFilter(dateToRollBackTo, ranChangeSetList),
new AlreadyRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
if (rollbackScript == null) {
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
logIterator.run(createRollbackVisitor(), new RuntimeEnvironment(database, contexts, labelExpression));
});
} else {
List changeSets = determineRollbacks(logIterator, contexts, labelExpression);
Map values = new HashMap<>();
values.put(Scope.Attr.logService.name(), compositeLogService);
values.put(BufferedLogService.class.getName(), bufferLog);
Scope.child(values, () -> {
executeRollbackScript(rollbackScript, changeSets, contexts, labelExpression);
});
removeRunStatus(changeSets, contexts, labelExpression);
}
hubUpdater.postUpdateHub(rollbackOperation, bufferLog);
}
catch (Throwable t) {
if (hubUpdater != null) {
hubUpdater.postUpdateHubExceptionHandling(rollbackOperation, bufferLog, t.getMessage());
}
throw t;
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
setChangeExecListener(null);
}
}
});
}
public void changeLogSync(String contexts, Writer output) throws LiquibaseException {
changeLogSync(new Contexts(contexts), new LabelExpression(), output);
}
public void changeLogSync(Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
doChangeLogSyncSql(null, contexts, labelExpression, output,
() -> "SQL to add all changesets to database history table");
}
private void flushOutputWriter(Writer output) throws LiquibaseException {
if (output == null) {
return;
}
try {
output.flush();
} catch (IOException e) {
throw new LiquibaseException(e);
}
}
public void changeLogSync(String contexts) throws LiquibaseException {
changeLogSync(new Contexts(contexts), new LabelExpression());
}
/**
* @deprecated use version with LabelExpression
*/
@Deprecated
public void changeLogSync(Contexts contexts) throws LiquibaseException {
changeLogSync(contexts, new LabelExpression());
}
public void changeLogSync(Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
changeLogSync(null, contexts, labelExpression);
}
public void changeLogSync(String tag, String contexts) throws LiquibaseException {
changeLogSync(tag, new Contexts(contexts), new LabelExpression());
}
/**
*
* Changelogsync or changelogsync to tag
*
* @param tag
* @param contexts
* @param labelExpression
* @throws LiquibaseException
*
*/
public void changeLogSync(String tag, Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
Operation changeLogSyncOperation = null;
BufferedLogService bufferLog = new BufferedLogService();
DatabaseChangeLog changeLog = null;
HubUpdater hubUpdater = null;
try {
changeLog = getDatabaseChangeLog();
checkLiquibaseTables(true, changeLog, contexts, labelExpression);
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
changeLog.validate(database, contexts, labelExpression);
//
// Let the user know that they can register for Hub
//
hubUpdater = new HubUpdater(new Date(), changeLog, database);
hubUpdater.register(changeLogFile);
//
// Create an iterator which will be used with a ListVisitor
// to grab the list of change sets for the update
//
ChangeLogIterator listLogIterator = buildChangeLogIterator(tag, changeLog, contexts, labelExpression);
//
// Create or retrieve the Connection
// Make sure the Hub is available here by checking the return
//
Connection connection = getConnection(changeLog);
if (connection != null) {
String operationCommand = (tag == null ? "changelog-sync" : "changelog-sync-to-tag");
changeLogSyncOperation =
hubUpdater.preUpdateHub("CHANGELOGSYNC", operationCommand, connection, changeLogFile, contexts, labelExpression, listLogIterator);
}
//
// If we are doing Hub then set up a HubChangeExecListener
//
if (connection != null) {
changeLogSyncListener = new HubChangeExecListener(changeLogSyncOperation, changeExecListener);
}
ChangeLogIterator runChangeLogIterator = buildChangeLogIterator(tag, changeLog, contexts, labelExpression);
CompositeLogService compositeLogService = new CompositeLogService(true, bufferLog);
Scope.child(Scope.Attr.logService.name(), compositeLogService, () -> {
runChangeLogIterator.run(new ChangeLogSyncVisitor(database, changeLogSyncListener),
new RuntimeEnvironment(database, contexts, labelExpression));
});
hubUpdater.postUpdateHub(changeLogSyncOperation, bufferLog);
}
catch (Exception e) {
if (changeLogSyncOperation != null) {
hubUpdater.postUpdateHubExceptionHandling(changeLogSyncOperation, bufferLog, e.getMessage());
}
throw e;
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
setChangeExecListener(null);
}
}
});
}
public void changeLogSync(String tag, String contexts, Writer output) throws LiquibaseException {
changeLogSync(tag, new Contexts(contexts), new LabelExpression(), output);
}
public void changeLogSync(String tag, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
doChangeLogSyncSql(tag, contexts, labelExpression, output,
() -> "SQL to add changesets upto '" + tag + "' to database history table");
}
private void doChangeLogSyncSql(String tag, Contexts contexts, LabelExpression labelExpression, Writer output,
Supplier header) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(() -> {
LoggingExecutor outputTemplate = new LoggingExecutor(
Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(database), output, database
);
/* We have no other choice than to save the current Executer here. */
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("SQL to add all changesets to database history table");
changeLogSync(tag, contexts, labelExpression);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
});
}
public void markNextChangeSetRan(String contexts, Writer output) throws LiquibaseException {
markNextChangeSetRan(new Contexts(contexts), new LabelExpression(), output);
}
public void markNextChangeSetRan(Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
@SuppressWarnings("squid:S1941")
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
outputHeader("SQL to add all changesets to database history table");
markNextChangeSetRan(contexts, labelExpression);
flushOutputWriter(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
}
});
}
public void markNextChangeSetRan(String contexts) throws LiquibaseException {
markNextChangeSetRan(new Contexts(contexts), new LabelExpression());
}
public void markNextChangeSetRan(Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
changeLog.validate(database, contexts, labelExpression);
ChangeLogIterator logIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(1));
logIterator.run(new ChangeLogSyncVisitor(database),
new RuntimeEnvironment(database, contexts, labelExpression)
);
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
resetServices();
}
}
});
}
public void futureRollbackSQL(String contexts, Writer output) throws LiquibaseException {
futureRollbackSQL(null, contexts, output, true);
}
public void futureRollbackSQL(Writer output) throws LiquibaseException {
futureRollbackSQL(null, null, new Contexts(), new LabelExpression(), output);
}
public void futureRollbackSQL(String contexts, Writer output, boolean checkLiquibaseTables)
throws LiquibaseException {
futureRollbackSQL(null, contexts, output, checkLiquibaseTables);
}
public void futureRollbackSQL(Integer count, String contexts, Writer output) throws LiquibaseException {
futureRollbackSQL(count, new Contexts(contexts), new LabelExpression(), output, true);
}
public void futureRollbackSQL(Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
futureRollbackSQL(null, null, contexts, labelExpression, output);
}
public void futureRollbackSQL(Integer count, String contexts, Writer output, boolean checkLiquibaseTables)
throws LiquibaseException {
futureRollbackSQL(count, new Contexts(contexts), new LabelExpression(), output, checkLiquibaseTables);
}
public void futureRollbackSQL(Integer count, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
futureRollbackSQL(count, contexts, labelExpression, output, true);
}
public void futureRollbackSQL(Integer count, Contexts contexts, LabelExpression labelExpression, Writer output,
boolean checkLiquibaseTables) throws LiquibaseException {
futureRollbackSQL(count, null, contexts, labelExpression, output);
}
public void futureRollbackSQL(String tag, Contexts contexts, LabelExpression labelExpression, Writer output)
throws LiquibaseException {
futureRollbackSQL(null, tag, contexts, labelExpression, output);
}
protected void futureRollbackSQL(Integer count, String tag, Contexts contexts, LabelExpression labelExpression,
Writer output) throws LiquibaseException {
futureRollbackSQL(count, tag, contexts, labelExpression, output, true);
}
protected void futureRollbackSQL(Integer count, String tag, Contexts contexts, LabelExpression labelExpression,
Writer output, boolean checkLiquibaseTables) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LoggingExecutor outputTemplate = new LoggingExecutor(Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor(database),
output, database);
Executor oldTemplate = getAndReplaceJdbcExecutor(output);
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor(database, outputTemplate);
outputHeader("SQL to roll back currently unexecuted changes");
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
if (checkLiquibaseTables) {
checkLiquibaseTables(false, changeLog, contexts, labelExpression);
}
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
changeLog.validate(database, contexts, labelExpression);
ChangeLogIterator logIterator;
if ((count == null) && (tag == null)) {
logIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new IgnoreChangeSetFilter(),
new DbmsChangeSetFilter(database));
} else if (count != null) {
ChangeLogIterator forwardIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new CountChangeSetFilter(count));
final ListVisitor listVisitor = new ListVisitor();
forwardIterator.run(listVisitor, new RuntimeEnvironment(database, contexts, labelExpression));
logIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(database.getRanChangeSetList()),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new ChangeSetFilter() {
@Override
public ChangeSetFilterResult accepts(ChangeSet changeSet) {
return new ChangeSetFilterResult(
listVisitor.getSeenChangeSets().contains(changeSet), null, null
);
}
});
} else {
List ranChangeSetList = database.getRanChangeSetList();
UpToTagChangeSetFilter upToTagChangeSetFilter = new UpToTagChangeSetFilter(tag, ranChangeSetList);
ChangeLogIterator forwardIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
upToTagChangeSetFilter);
final ListVisitor listVisitor = new ListVisitor();
forwardIterator.run(listVisitor, new RuntimeEnvironment(database, contexts, labelExpression));
//
// Check to see if the tag was found and stop if not
//
if (! upToTagChangeSetFilter.isSeenTag()) {
String message = "No tag matching '" + tag + "' found";
Scope.getCurrentScope().getUI().sendMessage("ERROR: " + message);
Scope.getCurrentScope().getLog(Liquibase.class).severe(message);
throw new LiquibaseException(new IllegalArgumentException(message));
}
logIterator = new ChangeLogIterator(changeLog,
new NotRanChangeSetFilter(ranChangeSetList),
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter(),
new ChangeSetFilter() {
@Override
public ChangeSetFilterResult accepts(ChangeSet changeSet) {
return new ChangeSetFilterResult(
listVisitor.getSeenChangeSets().contains(changeSet), null, null
);
}
});
}
logIterator.run(createRollbackVisitor(),
new RuntimeEnvironment(database, contexts, labelExpression)
);
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
Scope.getCurrentScope().getSingleton(ExecutorService.class).setExecutor("jdbc", database, oldTemplate);
resetServices();
}
flushOutputWriter(output);
}
});
}
protected void resetServices() {
LockServiceFactory.getInstance().resetAll();
ChangeLogHistoryServiceFactory.getInstance().resetAll();
Scope.getCurrentScope().getSingleton(ExecutorService.class).reset();
}
/**
* Drops all database objects in the default schema.
*/
public final void dropAll() throws DatabaseException {
dropAll(new CatalogAndSchema(getDatabase().getDefaultCatalogName(), getDatabase().getDefaultSchemaName()));
}
/**
* Drops all database objects in the passed schema(s).
*/
public final void dropAll(CatalogAndSchema... schemas) throws DatabaseException {
if ((schemas == null) || (schemas.length == 0)) {
schemas = new CatalogAndSchema[]{
new CatalogAndSchema(getDatabase().getDefaultCatalogName(), getDatabase().getDefaultSchemaName())
};
}
CatalogAndSchema[] finalSchemas = schemas;
try {
CommandScope dropAll = new CommandScope("internalDropAll")
.addArgumentValue(InternalDropAllCommandStep.DATABASE_ARG, Liquibase.this.getDatabase())
.addArgumentValue(InternalDropAllCommandStep.SCHEMAS_ARG, finalSchemas);
try {
dropAll.execute();
} catch (CommandExecutionException e) {
throw new DatabaseException(e);
}
} catch (LiquibaseException e) {
if (e instanceof DatabaseException) {
throw (DatabaseException) e;
} else {
throw new DatabaseException(e);
}
}
}
/**
* 'Tags' the database for future rollback
*/
public void tag(String tagString) throws LiquibaseException {
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(database).generateDeploymentId();
checkLiquibaseTables(false, null, new Contexts(),
new LabelExpression());
getDatabase().tag(tagString);
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
}
}
});
}
public boolean tagExists(String tagString) throws LiquibaseException {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
checkLiquibaseTables(false, null, new Contexts(),
new LabelExpression());
return getDatabase().doesTagExist(tagString);
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
}
}
public void updateTestingRollback(String contexts) throws LiquibaseException {
updateTestingRollback(new Contexts(contexts), new LabelExpression());
}
public void updateTestingRollback(Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
updateTestingRollback(null, contexts, labelExpression);
}
public void updateTestingRollback(String tag, Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
Date baseDate = new Date();
update(tag, contexts, labelExpression);
rollback(baseDate, null, contexts, labelExpression);
update(tag, contexts, labelExpression);
}
public void checkLiquibaseTables(boolean updateExistingNullChecksums, DatabaseChangeLog databaseChangeLog,
Contexts contexts, LabelExpression labelExpression) throws LiquibaseException {
ChangeLogHistoryService changeLogHistoryService =
ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(getDatabase());
changeLogHistoryService.init();
if (updateExistingNullChecksums) {
changeLogHistoryService.upgradeChecksums(databaseChangeLog, contexts, labelExpression);
}
LockServiceFactory.getInstance().getLockService(getDatabase()).init();
}
/**
* Returns true if it is "save" to migrate the database.
* Currently, "safe" is defined as running in an output-sql mode or against a database on localhost.
* It is fine to run Liquibase against a "non-safe" database, the method is mainly used to determine if the user
* should be prompted before continuing.
*/
public boolean isSafeToRunUpdate() throws DatabaseException {
return getDatabase().isSafeToRunUpdate();
}
/**
* Display change log lock information.
*/
public DatabaseChangeLogLock[] listLocks() throws LiquibaseException {
checkLiquibaseTables(false, null, new Contexts(), new LabelExpression());
return LockServiceFactory.getInstance().getLockService(database).listLocks();
}
public void reportLocks(PrintStream out) throws LiquibaseException {
DatabaseChangeLogLock[] locks = listLocks();
out.println("Database change log locks for " + getDatabase().getConnection().getConnectionUserName()
+ "@" + getDatabase().getConnection().getURL());
if (locks.length == 0) {
out.println(" - No locks");
return;
}
for (DatabaseChangeLogLock lock : locks) {
out.println(" - " + lock.getLockedBy() + " at " +
DateFormat.getDateTimeInstance().format(lock.getLockGranted()));
}
out.println("NOTE: The lock time displayed is based on the database's configured time");
}
public void forceReleaseLocks() throws LiquibaseException {
checkLiquibaseTables(false, null, new Contexts(), new LabelExpression());
LockServiceFactory.getInstance().getLockService(database).forceReleaseLock();
}
/**
* @deprecated use version with LabelExpression
*/
@Deprecated
public List listUnrunChangeSets(Contexts contexts) throws LiquibaseException {
return listUnrunChangeSets(contexts, new LabelExpression());
}
public List listUnrunChangeSets(Contexts contexts, LabelExpression labels) throws LiquibaseException {
return listUnrunChangeSets(contexts, labels, true);
}
public List listUnrunChangeSets(Contexts contexts, LabelExpression labels, boolean checkLiquibaseTables) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labels);
ListVisitor visitor = new ListVisitor();
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
if (checkLiquibaseTables) {
checkLiquibaseTables(true, changeLog, contexts, labels);
}
changeLog.validate(database, contexts, labels);
ChangeLogIterator logIterator = getStandardChangelogIterator(contexts, labels, changeLog);
logIterator.run(visitor, new RuntimeEnvironment(database, contexts, labels));
}
});
return visitor.getSeenChangeSets();
}
/**
* @deprecated use version with LabelExpression
*/
@Deprecated
public List getChangeSetStatuses(Contexts contexts) throws LiquibaseException {
return getChangeSetStatuses(contexts, new LabelExpression());
}
public List getChangeSetStatuses(Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
return getChangeSetStatuses(contexts, labelExpression, true);
}
/**
* Returns the ChangeSetStatuses of all changesets in the change log file and history in the order they
* would be ran.
*/
public List getChangeSetStatuses(Contexts contexts, LabelExpression labelExpression,
boolean checkLiquibaseTables) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
StatusVisitor visitor = new StatusVisitor(database);
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
if (checkLiquibaseTables) {
checkLiquibaseTables(true, changeLog, contexts, labelExpression);
}
changeLog.validate(database, contexts, labelExpression);
ChangeLogIterator logIterator = getStandardChangelogIterator(contexts, labelExpression, changeLog);
logIterator.run(visitor, new RuntimeEnvironment(database, contexts, labelExpression));
}
});
return visitor.getStatuses();
}
public void reportStatus(boolean verbose, String contexts, Writer out) throws LiquibaseException {
reportStatus(verbose, new Contexts(contexts), new LabelExpression(), out);
}
public void reportStatus(boolean verbose, Contexts contexts, Writer out) throws LiquibaseException {
reportStatus(verbose, contexts, new LabelExpression(), out);
}
public void reportStatus(boolean verbose, Contexts contexts, LabelExpression labels, Writer out)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labels);
try {
List unrunChangeSets = listUnrunChangeSets(contexts, labels, false);
if (unrunChangeSets.isEmpty()) {
out.append(getDatabase().getConnection().getConnectionUserName());
out.append("@");
out.append(getDatabase().getConnection().getURL());
out.append(" is up to date");
out.append(StreamUtil.getLineSeparator());
} else {
out.append(String.valueOf(unrunChangeSets.size()));
out.append(" change sets have not been applied to ");
out.append(getDatabase().getConnection().getConnectionUserName());
out.append("@");
out.append(getDatabase().getConnection().getURL());
out.append(StreamUtil.getLineSeparator());
if (verbose) {
for (ChangeSet changeSet : unrunChangeSets) {
out.append(" ").append(changeSet.toString(false))
.append(StreamUtil.getLineSeparator());
}
}
}
out.flush();
} catch (IOException e) {
throw new LiquibaseException(e);
}
}
public Collection listUnexpectedChangeSets(String contexts) throws LiquibaseException {
return listUnexpectedChangeSets(new Contexts(contexts), new LabelExpression());
}
public Collection listUnexpectedChangeSets(Contexts contexts, LabelExpression labelExpression)
throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
ExpectedChangesVisitor visitor = new ExpectedChangesVisitor(database.getRanChangeSetList());
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
changeLog.validate(database, contexts, labelExpression);
ChangeLogIterator logIterator = new ChangeLogIterator(changeLog,
new ContextChangeSetFilter(contexts),
new LabelChangeSetFilter(labelExpression),
new DbmsChangeSetFilter(database),
new IgnoreChangeSetFilter());
logIterator.run(visitor, new RuntimeEnvironment(database, contexts, labelExpression));
}
});
return visitor.getUnexpectedChangeSets();
}
public void reportUnexpectedChangeSets(boolean verbose, String contexts, Writer out) throws LiquibaseException {
reportUnexpectedChangeSets(verbose, new Contexts(contexts), new LabelExpression(), out);
}
public void reportUnexpectedChangeSets(boolean verbose, Contexts contexts, LabelExpression labelExpression,
Writer out) throws LiquibaseException {
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
try {
Collection unexpectedChangeSets = listUnexpectedChangeSets(contexts, labelExpression);
if (unexpectedChangeSets.isEmpty()) {
out.append(getDatabase().getConnection().getConnectionUserName());
out.append("@");
out.append(getDatabase().getConnection().getURL());
out.append(" contains no unexpected changes!");
out.append(StreamUtil.getLineSeparator());
} else {
out.append(String.valueOf(unexpectedChangeSets.size()));
out.append(" unexpected changes were found in ");
out.append(getDatabase().getConnection().getConnectionUserName());
out.append("@");
out.append(getDatabase().getConnection().getURL());
out.append(StreamUtil.getLineSeparator());
if (verbose) {
for (RanChangeSet ranChangeSet : unexpectedChangeSets) {
out.append(" ").append(ranChangeSet.toString()).append(StreamUtil.getLineSeparator());
}
}
}
out.flush();
} catch (IOException e) {
throw new LiquibaseException(e);
}
}
/**
* Sets checksums to null so they will be repopulated next run
*/
public void clearCheckSums() throws LiquibaseException {
LOG.info("Clearing database change log checksums");
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
checkLiquibaseTables(false, null, new Contexts(), new LabelExpression());
UpdateStatement updateStatement = new UpdateStatement(
getDatabase().getLiquibaseCatalogName(),
getDatabase().getLiquibaseSchemaName(),
getDatabase().getDatabaseChangeLogTableName()
);
updateStatement.addNewColumnValue("MD5SUM", null);
Scope.getCurrentScope().getSingleton(ExecutorService.class).getExecutor("jdbc", database).execute(updateStatement);
getDatabase().commit();
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
}
resetServices();
}
});
}
public final CheckSum calculateCheckSum(final String changeSetIdentifier) throws LiquibaseException {
if (changeSetIdentifier == null) {
throw new LiquibaseException(new IllegalArgumentException("changeSetIdentifier"));
}
final List parts = StringUtil.splitAndTrim(changeSetIdentifier, "::");
if ((parts == null) || (parts.size() < CHANGESET_ID_NUM_PARTS)) {
throw new LiquibaseException(
new IllegalArgumentException("Invalid changeSet identifier: " + changeSetIdentifier)
);
}
return this.calculateCheckSum(parts.get(CHANGESET_ID_CHANGELOG_PART),
parts.get(CHANGESET_ID_CHANGESET_PART), parts.get(CHANGESET_ID_AUTHOR_PART));
}
public CheckSum calculateCheckSum(final String filename, final String id, final String author)
throws LiquibaseException {
LOG.info(String.format("Calculating checksum for changeset %s::%s::%s", filename, id, author));
final ChangeLogParameters clParameters = this.getChangeLogParameters();
final ResourceAccessor resourceAccessor = this.getResourceAccessor();
final DatabaseChangeLog changeLog =
ChangeLogParserFactory.getInstance().getParser(
this.changeLogFile, resourceAccessor
).parse(this.changeLogFile, clParameters, resourceAccessor);
// TODO: validate?
final ChangeSet changeSet = changeLog.getChangeSet(filename, author, id);
if (changeSet == null) {
throw new LiquibaseException(
new IllegalArgumentException("No such changeSet: " + filename + "::" + id + "::" + author)
);
}
return changeSet.generateCheckSum();
}
public void generateDocumentation(String outputDirectory) throws LiquibaseException {
// call without context
generateDocumentation(outputDirectory, new Contexts(), new LabelExpression());
}
public void generateDocumentation(String outputDirectory, String contexts) throws LiquibaseException {
generateDocumentation(outputDirectory, new Contexts(contexts), new LabelExpression());
}
public void generateDocumentation(String outputDirectory, Contexts contexts,
LabelExpression labelExpression) throws LiquibaseException {
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
LOG.info("Generating Database Documentation");
changeLogParameters.setContexts(contexts);
changeLogParameters.setLabels(labelExpression);
LockService lockService = LockServiceFactory.getInstance().getLockService(database);
lockService.waitForLock();
try {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
checkLiquibaseTables(false, changeLog, new Contexts(), new LabelExpression());
changeLog.validate(database, contexts, labelExpression);
ChangeLogIterator logIterator = new ChangeLogIterator(changeLog,
new DbmsChangeSetFilter(database));
DBDocVisitor visitor = new DBDocVisitor(database);
logIterator.run(visitor, new RuntimeEnvironment(database, contexts, labelExpression));
visitor.writeHTML(new File(outputDirectory), resourceAccessor);
} catch (IOException e) {
throw new LiquibaseException(e);
} finally {
try {
lockService.releaseLock();
} catch (LockException e) {
LOG.severe(MSG_COULD_NOT_RELEASE_LOCK, e);
}
}
}
});
}
public DiffResult diff(Database referenceDatabase, Database targetDatabase, CompareControl compareControl)
throws LiquibaseException {
return DiffGeneratorFactory.getInstance().compare(referenceDatabase, targetDatabase, compareControl);
}
/**
* Checks changelogs for bad MD5Sums and preconditions before attempting a migration
*/
public void validate() throws LiquibaseException {
DatabaseChangeLog changeLog = getDatabaseChangeLog();
changeLog.validate(database);
}
public void setChangeLogParameter(String key, Object value) {
this.changeLogParameters.set(key, value);
}
/**
* Add safe database properties as changelog parameters.
* Safe properties are the ones that doesn't have side effects in liquibase state and also don't change in during the liquibase execution
*
* @param database Database which propeties are put in the changelog
* @throws DatabaseException
*/
private void setDatabasePropertiesAsChangelogParameters(Database database) throws DatabaseException {
setChangeLogParameter("database.autoIncrementClause", database.getAutoIncrementClause(null, null, null, null));
setChangeLogParameter("database.currentDateTimeFunction", database.getCurrentDateTimeFunction());
setChangeLogParameter("database.databaseChangeLogLockTableName", database.getDatabaseChangeLogLockTableName());
setChangeLogParameter("database.databaseChangeLogTableName", database.getDatabaseChangeLogTableName());
setChangeLogParameter("database.databaseMajorVersion", database.getDatabaseMajorVersion());
setChangeLogParameter("database.databaseMinorVersion", database.getDatabaseMinorVersion());
setChangeLogParameter("database.databaseProductName", database.getDatabaseProductName());
setChangeLogParameter("database.databaseProductVersion", database.getDatabaseProductVersion());
setChangeLogParameter("database.defaultCatalogName", database.getDefaultCatalogName());
setChangeLogParameter("database.defaultSchemaName", database.getDefaultSchemaName());
setChangeLogParameter("database.defaultSchemaNamePrefix", StringUtil.trimToNull(database.getDefaultSchemaName()) == null ? "" : "." + database.getDefaultSchemaName());
setChangeLogParameter("database.lineComment", database.getLineComment());
setChangeLogParameter("database.liquibaseSchemaName", database.getLiquibaseSchemaName());
setChangeLogParameter("database.liquibaseTablespaceName", database.getLiquibaseTablespaceName());
setChangeLogParameter("database.typeName", database.getShortName());
setChangeLogParameter("database.isSafeToRunUpdate", database.isSafeToRunUpdate());
setChangeLogParameter("database.requiresPassword", database.requiresPassword());
setChangeLogParameter("database.requiresUsername", database.requiresUsername());
setChangeLogParameter("database.supportsForeignKeyDisable", database.supportsForeignKeyDisable());
setChangeLogParameter("database.supportsInitiallyDeferrableColumns", database.supportsInitiallyDeferrableColumns());
setChangeLogParameter("database.supportsRestrictForeignKeys", database.supportsRestrictForeignKeys());
setChangeLogParameter("database.supportsSchemas", database.supportsSchemas());
setChangeLogParameter("database.supportsSequences", database.supportsSequences());
setChangeLogParameter("database.supportsTablespaces", database.supportsTablespaces());
}
private LockService getLockService() {
return LockServiceFactory.getInstance().getLockService(database);
}
public void setChangeExecListener(ChangeExecListener listener) {
this.changeExecListener = listener;
}
public void setChangeLogSyncListener(ChangeLogSyncListener changeLogSyncListener) {
this.changeLogSyncListener = changeLogSyncListener;
}
@SafeVarargs
public final void generateChangeLog(CatalogAndSchema catalogAndSchema, DiffToChangeLog changeLogWriter,
PrintStream outputStream, Class extends DatabaseObject>... snapshotTypes)
throws DatabaseException, IOException, ParserConfigurationException {
generateChangeLog(catalogAndSchema, changeLogWriter, outputStream, null, snapshotTypes);
}
@SafeVarargs
public final void generateChangeLog(CatalogAndSchema catalogAndSchema, DiffToChangeLog changeLogWriter,
PrintStream outputStream, ChangeLogSerializer changeLogSerializer,
Class extends DatabaseObject>... snapshotTypes)
throws DatabaseException, IOException, ParserConfigurationException {
try {
runInScope(new Scope.ScopedRunner() {
@Override
public void run() throws Exception {
Set> finalCompareTypes = null;
if ((snapshotTypes != null) && (snapshotTypes.length > 0)) {
finalCompareTypes = new HashSet<>(Arrays.asList(snapshotTypes));
}
SnapshotControl snapshotControl = new SnapshotControl(Liquibase.this.getDatabase(), snapshotTypes);
CompareControl compareControl = new CompareControl(new CompareControl.SchemaComparison[]{
new CompareControl.SchemaComparison(catalogAndSchema, catalogAndSchema)
}, finalCompareTypes);
DatabaseSnapshot originalDatabaseSnapshot = null;
try {
originalDatabaseSnapshot = SnapshotGeneratorFactory.getInstance().createSnapshot(
compareControl.getSchemas(CompareControl.DatabaseRole.REFERENCE),
getDatabase(),
snapshotControl
);
DiffResult diffResult = DiffGeneratorFactory.getInstance().compare(
originalDatabaseSnapshot,
SnapshotGeneratorFactory.getInstance().createSnapshot(
compareControl.getSchemas(CompareControl.DatabaseRole.REFERENCE),
null,
snapshotControl
),
compareControl
);
changeLogWriter.setDiffResult(diffResult);
if (changeLogSerializer != null) {
changeLogWriter.print(outputStream, changeLogSerializer);
} else {
changeLogWriter.print(outputStream);
}
} catch (InvalidExampleException e) {
throw new UnexpectedLiquibaseException(e);
}
}
});
} catch (LiquibaseException e) {
throw new DatabaseException(e);
}
}
private void runInScope(Scope.ScopedRunner scopedRunner) throws LiquibaseException {
Map scopeObjects = new HashMap<>();
scopeObjects.put(Scope.Attr.database.name(), getDatabase());
scopeObjects.put(Scope.Attr.resourceAccessor.name(), getResourceAccessor());
try {
Scope.child(scopeObjects, scopedRunner);
} catch (Exception e) {
if (e instanceof LiquibaseException) {
throw (LiquibaseException) e;
} else {
throw new LiquibaseException(e);
}
}
}
@Override
public void close() throws LiquibaseException {
if (database != null) {
database.close();
}
}
}