public void rollback(Database database) throws RollbackFailedException { try { Executor executor = ExecutorService.getInstance().getExecutor(database); executor.comment("Rolling Back ChangeSet: " + toString()); database.setObjectQuotingStrategy(objectQuotingStrategy); // set auto-commit based on runInTransaction if database supports DDL in transactions if (database.supportsDDLInTransaction()) { database.setAutoCommit(!runInTransaction); } RanChangeSet ranChangeSet = database.getRanChangeSet(this); if (hasCustomRollbackChanges()) { final List<SqlStatement> statements = new LinkedList<SqlStatement>(); for (Change change : rollback.getChanges()) { if (((change instanceof DbmsTargetedChange)) && !DatabaseList.definitionMatches( ((DbmsTargetedChange) change).getDbms(), database, true)) { continue; } SqlStatement[] changeStatements = change.generateStatements(database); if (changeStatements != null) { statements.addAll(Arrays.asList(changeStatements)); } } if (!statements.isEmpty()) { database.executeRollbackStatements( statements.toArray(new SqlStatement[] {}), sqlVisitors); } } else { List<Change> changes = getChanges(); for (int i = changes.size() - 1; i >= 0; i--) { Change change = changes.get(i); database.executeRollbackStatements(change, sqlVisitors); } } if (runInTransaction) { database.commit(); } log.debug("ChangeSet " + toString() + " has been successfully rolled back."); } catch (Exception e) { try { database.rollback(); } catch (DatabaseException e1) { // ok } throw new RollbackFailedException(e); } finally { // restore auto-commit to false if this ChangeSet was not run in a transaction, // but only if the database supports DDL in transactions if (!runInTransaction && database.supportsDDLInTransaction()) { try { database.setAutoCommit(false); } catch (DatabaseException e) { throw new RollbackFailedException("Could not resetInternalState autocommit", e); } } } }
/** * This method will actually execute each of the changes in the list against the specified * database. * * @return should change set be marked as ran */ public ExecType execute( DatabaseChangeLog databaseChangeLog, ChangeExecListener listener, Database database) throws MigrationFailedException { if (validationFailed) { return ExecType.MARK_RAN; } long startTime = new Date().getTime(); ExecType execType = null; boolean skipChange = false; Executor executor = ExecutorService.getInstance().getExecutor(database); try { // set object quoting strategy database.setObjectQuotingStrategy(objectQuotingStrategy); // set auto-commit based on runInTransaction if database supports DDL in transactions if (database.supportsDDLInTransaction()) { database.setAutoCommit(!runInTransaction); } executor.comment("Changeset " + toString(false)); if (StringUtils.trimToNull(getComments()) != null) { String comments = getComments(); String[] lines = comments.split("\\n"); for (int i = 0; i < lines.length; i++) { if (i > 0) { lines[i] = database.getLineComment() + " " + lines[i]; } } executor.comment(StringUtils.join(Arrays.asList(lines), "\n")); } try { if (preconditions != null) { preconditions.check(database, databaseChangeLog, this); } } catch (PreconditionFailedException e) { if (listener != null) { listener.preconditionFailed(e, preconditions.getOnFail()); } StringBuffer message = new StringBuffer(); message.append(StreamUtil.getLineSeparator()); for (FailedPrecondition invalid : e.getFailedPreconditions()) { message.append(" ").append(invalid.toString()); message.append(StreamUtil.getLineSeparator()); } if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.HALT)) { throw new MigrationFailedException(this, message.toString(), e); } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.CONTINUE)) { skipChange = true; execType = ExecType.SKIPPED; LogFactory.getLogger() .info( "Continuing past: " + toString() + " despite precondition failure due to onFail='CONTINUE': " + message); } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.MARK_RAN)) { execType = ExecType.MARK_RAN; skipChange = true; log.info( "Marking ChangeSet: " + toString() + " ran despite precondition failure due to onFail='MARK_RAN': " + message); } else if (preconditions.getOnFail().equals(PreconditionContainer.FailOption.WARN)) { execType = null; // already warned } else { throw new UnexpectedLiquibaseException( "Unexpected precondition onFail attribute: " + preconditions.getOnFail(), e); } } catch (PreconditionErrorException e) { if (listener != null) { listener.preconditionErrored(e, preconditions.getOnError()); } StringBuffer message = new StringBuffer(); message.append(StreamUtil.getLineSeparator()); for (ErrorPrecondition invalid : e.getErrorPreconditions()) { message.append(" ").append(invalid.toString()); message.append(StreamUtil.getLineSeparator()); } if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.HALT)) { throw new MigrationFailedException(this, message.toString(), e); } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.CONTINUE)) { skipChange = true; execType = ExecType.SKIPPED; } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.MARK_RAN)) { execType = ExecType.MARK_RAN; skipChange = true; log.info( "Marking ChangeSet: " + toString() + " ran despite precondition error: " + message); } else if (preconditions.getOnError().equals(PreconditionContainer.ErrorOption.WARN)) { execType = null; // already logged } else { throw new UnexpectedLiquibaseException( "Unexpected precondition onError attribute: " + preconditions.getOnError(), e); } database.rollback(); } finally { database.rollback(); } if (!skipChange) { for (Change change : changes) { try { change.finishInitialization(); } catch (SetupException se) { throw new MigrationFailedException(this, se); } } log.debug("Reading ChangeSet: " + toString()); for (Change change : getChanges()) { if ((!(change instanceof DbmsTargetedChange)) || DatabaseList.definitionMatches( ((DbmsTargetedChange) change).getDbms(), database, true)) { if (listener != null) { listener.willRun(change, this, changeLog, database); } if (change.generateStatementsVolatile(database)) { executor.comment( "WARNING The following SQL is possibly incorrect, invalid, and/or may change on each run:"); } database.executeStatements(change, databaseChangeLog, sqlVisitors); log.info(change.getConfirmationMessage()); if (listener != null) { listener.ran(change, this, changeLog, database); } } else { log.debug( "Change " + change.getSerializedObjectName() + " not included for database " + database.getShortName()); } } if (runInTransaction) { database.commit(); } log.info( "ChangeSet " + toString(false) + " ran successfully in " + (new Date().getTime() - startTime + "ms")); if (execType == null) { execType = ExecType.EXECUTED; } } else { log.debug("Skipping ChangeSet: " + toString()); } } catch (Exception e) { try { database.rollback(); } catch (Exception e1) { throw new MigrationFailedException(this, e); } if (getFailOnError() != null && !getFailOnError()) { log.info( "Change set " + toString(false) + " failed, but failOnError was false. Error: " + e.getMessage()); log.debug("Failure Stacktrace", e); execType = ExecType.FAILED; } else { // just log the message, dont log the stacktrace by appending exception. Its logged anyway // to stdout log.severe("Change Set " + toString(false) + " failed. Error: " + e.getMessage()); if (e instanceof MigrationFailedException) { throw ((MigrationFailedException) e); } else { throw new MigrationFailedException(this, e); } } } finally { // restore auto-commit to false if this ChangeSet was not run in a transaction, // but only if the database supports DDL in transactions if (!runInTransaction && database.supportsDDLInTransaction()) { try { database.setAutoCommit(false); } catch (DatabaseException e) { throw new MigrationFailedException(this, "Could not resetInternalState autocommit", e); } } } return execType; }