public boolean isSameAs(ChangeSet changeSet) { return this.getChangeLog() .replace('\\', '/') .equalsIgnoreCase(changeSet.getFilePath().replace('\\', '/')) && this.getId().equalsIgnoreCase(changeSet.getId()) && this.getAuthor().equalsIgnoreCase(changeSet.getAuthor()); }
public void printNonRenames(DatabaseSnapshot reference, DatabaseSnapshot target, StringBuilder sb) throws Exception { CompareControl compareControl = new CompareControl(reference.getSnapshotControl().getTypesToInclude()); DiffResult diffResult = DiffGeneratorFactory.getInstance().compare(reference, target, compareControl); DiffToChangeLog diffToChangeLog = new DiffToChangeLog(diffResult, new DiffOutputControl(false, false, false)); SqlGeneratorFactory generatorFactory = SqlGeneratorFactory.getInstance(); for (ChangeSet changeSet : diffToChangeLog.generateChangeSets()) { for (Change change : changeSet.getChanges()) { for (SqlStatement sqlStatement : change.generateStatements(LiquibaseModelFactory.DATABASE)) { for (Sql sql : generatorFactory.generateSql(sqlStatement, LiquibaseModelFactory.DATABASE)) { final String sqlString = sql.toSql(); if (sqlString.endsWith("DROP INDEX")) { sb.append(StringUtils.substringBefore(sqlString, " DROP INDEX")).append(";\n"); } else { sb.append(sqlString).append(";\n"); } } } } } }
public RanChangeSet(ChangeSet changeSet, ChangeSet.ExecType execType) { this( changeSet.getFilePath(), changeSet.getId(), changeSet.getAuthor(), changeSet.generateCheckSum(), new Date(), null, execType, changeSet.getDescription()); }
@Test public void parseAndGenerate() throws Exception { Database database = liquiBase.getDatabase(); ResourceAccessor resourceAccessor = new ClassLoaderResourceAccessor(); ChangeLogParameters changeLogParameters = new ChangeLogParameters(); DatabaseChangeLog changeLog = ChangeLogParserFactory.getInstance() .getParser(changeLogFile, resourceAccessor) .parse(changeLogFile, changeLogParameters, resourceAccessor); database.checkDatabaseChangeLogTable(false, changeLog, null); changeLog.validate(database); List<ChangeSet> changeSets = changeLog.getChangeSets(); List<String> expectedQuery = new ArrayList<String>(); expectedQuery.add( "MERGE INTO LIQUIBASE.myTable2 m " + "USING LIQUIBASE.myTable d " + "ON (m.pid=d.pid) " + "WHEN MATCHED THEN UPDATE SET m.sales=m.sales+d.sales,m.status=d.status " + "DELETE WHERE (m.status='OBS') " + "WHEN NOT MATCHED THEN INSERT VALUES(d.pid,d.sales,'OLD')"); int i = 0; for (ChangeSet changeSet : changeSets) { for (Change change : changeSet.getChanges()) { Sql[] sql = SqlGeneratorFactory.getInstance() .generateSql(change.generateStatements(database)[0], database); if (i == 3) { assertEquals(expectedQuery.get(0), sql[0].toSql()); } } i++; } }
@Override public void check(Database database, DatabaseChangeLog changeLog, ChangeSet changeSet) throws PreconditionFailedException, PreconditionErrorException { ObjectQuotingStrategy objectQuotingStrategy = null; if (changeSet == null) { objectQuotingStrategy = ObjectQuotingStrategy.LEGACY; } else { objectQuotingStrategy = changeSet.getObjectQuotingStrategy(); } String changeLogFile = getChangeLogFile(); if (changeLogFile == null) { changeLogFile = changeLog.getLogicalFilePath(); } ChangeSet interestedChangeSet = new ChangeSet( getId(), getAuthor(), false, false, changeLogFile, null, null, false, objectQuotingStrategy, changeLog); RanChangeSet ranChangeSet; try { ranChangeSet = database.getRanChangeSet(interestedChangeSet); } catch (Exception e) { throw new PreconditionErrorException(e, changeLog, this); } if (ranChangeSet == null || ranChangeSet.getExecType() == null || !ranChangeSet.getExecType().ran) { throw new PreconditionFailedException( "Change Set '" + interestedChangeSet.toString(false) + "' has not been run", changeLog, this); } }
/** Returns the run status for the given ChangeSet */ @Override public ChangeSet.RunStatus getRunStatus(ChangeSet changeSet) throws DatabaseException, DatabaseHistoryException { if (!hasDatabaseChangeLogTable()) { return ChangeSet.RunStatus.NOT_RAN; } RanChangeSet foundRan = getRanChangeSet(changeSet); if (foundRan == null) { return ChangeSet.RunStatus.NOT_RAN; } else { if (foundRan.getLastCheckSum() == null) { try { LogFactory.getLogger().info("Updating NULL md5sum for " + changeSet.toString()); ExecutorService.getInstance() .getExecutor(this) .execute( new RawSqlStatement( "UPDATE " + escapeTableName( getLiquibaseSchemaName(), getDatabaseChangeLogTableName()) + " SET MD5SUM='" + changeSet.generateCheckSum().toString() + "' WHERE ID='" + changeSet.getId() + "' AND AUTHOR='" + changeSet.getAuthor() + "' AND FILENAME='" + changeSet.getFilePath() + "'")); this.commit(); } catch (DatabaseException e) { throw new DatabaseException(e); } return ChangeSet.RunStatus.ALREADY_RAN; } else { if (foundRan.getLastCheckSum().equals(changeSet.generateCheckSum())) { return ChangeSet.RunStatus.ALREADY_RAN; } else { if (changeSet.shouldRunOnChange()) { return ChangeSet.RunStatus.RUN_AGAIN; } else { return ChangeSet.RunStatus.INVALID_MD5SUM; // throw new DatabaseHistoryException("MD5 Check for " + changeSet.toString() + " // failed"); } } } } }
@Override public void setExecType(ChangeSet changeSet, ChangeSet.ExecType execType, Scope scope) throws LiquibaseException { Logger log = LoggerFactory.getLogger(getClass()); if (execType == ChangeSet.ExecType.FAILED) { log.info("Not marking failed changeSet as ran"); return; } if (execType == ChangeSet.ExecType.SKIPPED) { log.info("Not marking skipped changeSet as ran"); return; } Map<String, Object> data = new LinkedHashMap<>(); data.put(correctCase("id"), changeSet.id); data.put(correctCase("author"), changeSet.author); data.put(correctCase("filename"), changeSet.getPath()); data.put(correctCase("dateexecuted"), scope.getDatabase().getCurrentDateTimeFunction(scope)); data.put(correctCase("md5sum"), "TODO"); data.put(correctCase("exectype"), execType.value); data.put(correctCase("dateexecuted"), scope.getDatabase().getCurrentDateTimeFunction(scope)); data.put(correctCase("orderexecuted"), 1); // todo data.put(correctCase("description"), "TODO"); // todo data.put(correctCase("comments"), "TODO"); // todo data.put(correctCase("contexts"), "TODO"); // todo data.put(correctCase("labels"), "TODO"); // todo data.put(correctCase("liquibase"), "TODO"); // todo data.put(correctCase("deployment_id"), "TODO"); // todo scope .getSingleton(ActionExecutor.class) .execute( new InsertDataAction( new RowData( ((StandardChangeLogHistoryService) scope.get( Scope.Attr.changeLogHistoryService, ChangeLogHistoryService.class)) .changeLogTable, data)), scope); }
@Override public Sql[] generateSql( MarkChangeSetRanStatement statement, Database database, SqlGeneratorChain sqlGeneratorChain) { String dateValue = database.getCurrentDateTimeFunction(); ChangeSet changeSet = statement.getChangeSet(); SqlStatement runStatement; try { if (statement.getExecType().equals(ChangeSet.ExecType.FAILED) || statement.getExecType().equals(ChangeSet.ExecType.SKIPPED)) { return new Sql[0]; // don't mark } else if (statement.getExecType().ranBefore) { runStatement = new UpdateStatement( database.getLiquibaseCatalogName(), database.getLiquibaseSchemaName(), database.getDatabaseChangeLogTableName()) .addNewColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue)) .addNewColumnValue("MD5SUM", changeSet.generateCheckSum().toString()) .addNewColumnValue("EXECTYPE", statement.getExecType().value) .setWhereClause("ID=? AND AUTHOR=? AND FILENAME=?") .addWhereParameters( changeSet.getId(), changeSet.getAuthor(), changeSet.getFilePath()); } else { runStatement = new InsertStatement( database.getLiquibaseCatalogName(), database.getLiquibaseSchemaName(), database.getDatabaseChangeLogTableName()) .addColumnValue("ID", changeSet.getId()) .addColumnValue("AUTHOR", changeSet.getAuthor()) .addColumnValue("FILENAME", changeSet.getFilePath()) .addColumnValue("DATEEXECUTED", new DatabaseFunction(dateValue)) .addColumnValue( "ORDEREXECUTED", ChangeLogHistoryServiceFactory.getInstance() .getChangeLogService(database) .getNextSequenceValue()) .addColumnValue("MD5SUM", changeSet.generateCheckSum().toString()) .addColumnValue("DESCRIPTION", limitSize(changeSet.getDescription())) .addColumnValue( "COMMENTS", limitSize( database.escapeStringForDatabase( StringUtils.trimToEmpty(changeSet.getComments())))) .addColumnValue("EXECTYPE", statement.getExecType().value) .addColumnValue( "LIQUIBASE", LiquibaseUtil.getBuildVersion().replaceAll("SNAPSHOT", "SNP")); String tag = null; List<Change> changes = changeSet.getChanges(); if (changes != null && changes.size() == 1) { Change change = changes.get(0); if (change instanceof TagDatabaseChange) { TagDatabaseChange tagChange = (TagDatabaseChange) change; tag = tagChange.getTag(); } } if (tag != null) { ((InsertStatement) runStatement).addColumnValue("TAG", tag); } } } catch (LiquibaseException e) { throw new UnexpectedLiquibaseException(e); } return SqlGeneratorFactory.getInstance().generateSql(runStatement, database); }
/** Drops all objects owned by the connected user. */ @Override public void dropDatabaseObjects(final CatalogAndSchema schemaToDrop) throws LiquibaseException { ObjectQuotingStrategy currentStrategy = this.getObjectQuotingStrategy(); this.setObjectQuotingStrategy(ObjectQuotingStrategy.QUOTE_ALL_OBJECTS); try { DatabaseSnapshot snapshot; try { final SnapshotControl snapshotControl = new SnapshotControl(this); final Set<Class<? extends DatabaseObject>> typesToInclude = snapshotControl.getTypesToInclude(); // We do not need to remove indexes and primary/unique keys explicitly. They should be // removed // as part of tables. typesToInclude.remove(Index.class); typesToInclude.remove(PrimaryKey.class); typesToInclude.remove(UniqueConstraint.class); if (supportsForeignKeyDisable()) { // We do not remove ForeignKey because they will be disabled and removed as parts of // tables. typesToInclude.remove(ForeignKey.class); } final long createSnapshotStarted = System.currentTimeMillis(); snapshot = SnapshotGeneratorFactory.getInstance() .createSnapshot(schemaToDrop, this, snapshotControl); LogFactory.getLogger() .debug( String.format( "Database snapshot generated in %d ms. Snapshot includes: %s", System.currentTimeMillis() - createSnapshotStarted, typesToInclude)); } catch (LiquibaseException e) { throw new UnexpectedLiquibaseException(e); } final long changeSetStarted = System.currentTimeMillis(); DiffResult diffResult = DiffGeneratorFactory.getInstance() .compare( new EmptyDatabaseSnapshot(this), snapshot, new CompareControl(snapshot.getSnapshotControl().getTypesToInclude())); List<ChangeSet> changeSets = new DiffToChangeLog( diffResult, new DiffOutputControl(true, true, false).addIncludedSchema(schemaToDrop)) .generateChangeSets(); LogFactory.getLogger() .debug( String.format( "ChangeSet to Remove Database Objects generated in %d ms.", System.currentTimeMillis() - changeSetStarted)); boolean previousAutoCommit = this.getAutoCommitMode(); this.commit(); // clear out currently executed statements this.setAutoCommit(false); // some DDL doesn't work in autocommit mode final boolean reEnableFK = supportsForeignKeyDisable() && disableForeignKeyChecks(); try { for (ChangeSet changeSet : changeSets) { changeSet.setFailOnError(false); for (Change change : changeSet.getChanges()) { if (change instanceof DropTableChange) { ((DropTableChange) change).setCascadeConstraints(true); } SqlStatement[] sqlStatements = change.generateStatements(this); for (SqlStatement statement : sqlStatements) { ExecutorService.getInstance().getExecutor(this).execute(statement); } } this.commit(); } } finally { if (reEnableFK) { enableForeignKeyChecks(); } } ChangeLogHistoryServiceFactory.getInstance().getChangeLogService(this).destroy(); LockServiceFactory.getInstance().getLockService(this).destroy(); this.setAutoCommit(previousAutoCommit); } finally { this.setObjectQuotingStrategy(currentStrategy); this.commit(); } }
protected void handleRollbackNode(ParsedNode rollbackNode, ResourceAccessor resourceAccessor) throws ParsedNodeException { String changeSetId = rollbackNode.getChildValue(null, "changeSetId", String.class); if (changeSetId != null) { String changeSetAuthor = rollbackNode.getChildValue(null, "changeSetAuthor", String.class); String changeSetPath = rollbackNode.getChildValue(null, "changeSetPath", getFilePath()); ChangeSet changeSet = this.getChangeLog().getChangeSet(changeSetPath, changeSetAuthor, changeSetId); if (changeSet == null) { // check from root changeSet = getChangeLog() .getRootChangeLog() .getChangeSet(changeSetPath, changeSetAuthor, changeSetId); if (changeSet == null) { throw new ParsedNodeException( "Change set " + new ChangeSet( changeSetId, changeSetAuthor, false, false, changeSetPath, null, null, null) .toString(false) + " does not exist"); } } for (Change change : changeSet.getChanges()) { rollback.getChanges().add(change); } return; } boolean foundValue = false; for (ParsedNode childNode : rollbackNode.getChildren()) { Change rollbackChange = toChange(childNode, resourceAccessor); if (rollbackChange != null) { addRollbackChange(rollbackChange); foundValue = true; } } Object value = rollbackNode.getValue(); if (value != null) { if (value instanceof String) { String finalValue = StringUtils.trimToNull((String) value); if (finalValue != null) { String[] strings = StringUtils.processMutliLineSQL(finalValue, true, true, ";"); for (String string : strings) { addRollbackChange(new RawSQLChange(string)); foundValue = true; } } } else { throw new ParsedNodeException( "Unexpected object: " + value.getClass().getName() + " '" + value.toString() + "'"); } } if (!foundValue) { addRollbackChange(new EmptyChange()); } }