public void repair() { if (!table.existsNoQuotes() && !table.exists()) { LOG.info( "Repair of metadata table " + table + " not necessary. No failed migration detected."); return; } createIfNotExists(); try { int failedCount = jdbcTemplate.queryForInt( "SELECT COUNT(*) FROM " + table + " WHERE " + dbSupport.quote("success") + "=" + dbSupport.getBooleanFalse()); if (failedCount == 0) { LOG.info( "Repair of metadata table " + table + " not necessary. No failed migration detected."); return; } } catch (SQLException e) { throw new FlywayException( "Unable to check the metadata table " + table + " for failed migrations", e); } StopWatch stopWatch = new StopWatch(); stopWatch.start(); try { jdbcTemplate.execute( "DELETE FROM " + table + " WHERE " + dbSupport.quote("success") + " = " + dbSupport.getBooleanFalse()); } catch (SQLException e) { throw new FlywayException("Unable to repair metadata table " + table, e); } stopWatch.stop(); LOG.info( "Metadata table " + table + " successfully repaired (execution time " + TimeFormat.format(stopWatch.getTotalTimeMillis()) + ")."); LOG.info("Manual cleanup of the remaining effects the failed migration may still be required."); }
/** Creates the metatable if it doesn't exist, upgrades it if it does. */ private void createIfNotExists() { if (table.existsNoQuotes() || table.exists()) { if (!upgraded) { new MetaDataTableTo20FormatUpgrader(dbSupport, table, migrationResolver).upgrade(); new MetaDataTableTo202FormatUpgrader(dbSupport, table).upgrade(); upgraded = true; } return; } LOG.info("Creating Metadata table: " + table); final String source = new ClassPathResource(dbSupport.getScriptLocation() + "createMetaDataTable.sql") .loadAsString("UTF-8"); Map<String, String> placeholders = new HashMap<String, String>(); placeholders.put("schema", table.getSchema().getName()); placeholders.put("table", table.getName()); final String sourceNoPlaceholders = new PlaceholderReplacer(placeholders, "${", "}").replacePlaceholders(source); SqlScript sqlScript = new SqlScript(sourceNoPlaceholders, dbSupport); sqlScript.execute(jdbcTemplate); LOG.debug("Metadata table " + table + " created."); }
public boolean hasInitMarker() { if (!table.existsNoQuotes() && !table.exists()) { return false; } createIfNotExists(); try { int count = jdbcTemplate.queryForInt( "SELECT COUNT(*) FROM " + table + " WHERE " + dbSupport.quote("type") + "='INIT'"); return count > 0; } catch (SQLException e) { throw new FlywayException( "Unable to check whether the metadata table " + table + " has an init marker migration", e); } }
/** * Calculate the rank for this new version about to be inserted. * * @param version The version to calculated for. * @return The rank. */ private int calculateVersionRank(MigrationVersion version) throws SQLException { List<String> versions = jdbcTemplate.queryForStringList("select " + dbSupport.quote("version") + " from " + table); List<MigrationVersion> migrationVersions = new ArrayList<MigrationVersion>(); for (String versionStr : versions) { migrationVersions.add(new MigrationVersion(versionStr)); } Collections.sort(migrationVersions); for (int i = 0; i < migrationVersions.size(); i++) { if (version.compareTo(migrationVersions.get(i)) < 0) { return i + 1; } } return migrationVersions.size() + 1; }
public boolean hasAppliedMigrations() { if (!table.existsNoQuotes() && !table.exists()) { return false; } createIfNotExists(); try { int count = jdbcTemplate.queryForInt( "SELECT COUNT(*) FROM " + table + " WHERE " + dbSupport.quote("type") + " NOT IN ('SCHEMA', 'INIT')"); return count > 0; } catch (SQLException e) { throw new FlywayException( "Unable to check whether the metadata table " + table + " has applied migrations", e); } }
public MigrationVersion getCurrentSchemaVersion() { if (!table.existsNoQuotes() && !table.exists()) { return MigrationVersion.EMPTY; } try { if (jdbcTemplate.queryForInt("SELECT COUNT(*) FROM " + table) == 0) { return MigrationVersion.EMPTY; } } catch (SQLException e) { throw new FlywayException( "Error checking if the metadata table " + table + " has at least one row", e); } createIfNotExists(); // Determine the version associated with the highest version_rank String query = "SELECT t1." + dbSupport.quote("version") + " FROM " + table + " t1" + " LEFT OUTER JOIN " + table + " t2 ON" + " (t1." + dbSupport.quote("version") + " = t2." + dbSupport.quote("version") + " AND t1." + dbSupport.quote("version_rank") + " < t2." + dbSupport.quote("version_rank") + ")" + " WHERE t2." + dbSupport.quote("version") + " IS NULL"; try { String version = jdbcTemplate.queryForString(query); return new MigrationVersion(version); } catch (SQLException e) { throw new FlywayException( "Error determining current schema version from metadata table " + table, e); } }
/** * Creates a new instance of the metadata table support. * * @param dbSupport Database-specific functionality. * @param table The metadata table used by flyway. * @param migrationResolver For resolving available migrations. */ public MetaDataTableImpl(DbSupport dbSupport, Table table, MigrationResolver migrationResolver) { this.jdbcTemplate = dbSupport.getJdbcTemplate(); this.dbSupport = dbSupport; this.table = table; this.migrationResolver = migrationResolver; }
public List<AppliedMigration> allAppliedMigrations() { if (!table.existsNoQuotes() && !table.exists()) { return new ArrayList<AppliedMigration>(); } createIfNotExists(); String query = "SELECT " + dbSupport.quote("version_rank") + "," + dbSupport.quote("installed_rank") + "," + dbSupport.quote("version") + "," + dbSupport.quote("description") + "," + dbSupport.quote("type") + "," + dbSupport.quote("script") + "," + dbSupport.quote("checksum") + "," + dbSupport.quote("installed_on") + "," + dbSupport.quote("installed_by") + "," + dbSupport.quote("execution_time") + "," + dbSupport.quote("success") + " FROM " + table + " ORDER BY " + dbSupport.quote("version_rank"); try { return jdbcTemplate.query( query, new RowMapper<AppliedMigration>() { public AppliedMigration mapRow(final ResultSet rs) throws SQLException { return new AppliedMigration( rs.getInt("version_rank"), rs.getInt("installed_rank"), new MigrationVersion(rs.getString("version")), rs.getString("description"), MigrationType.valueOf(rs.getString("type")), rs.getString("script"), toInteger((Number) rs.getObject("checksum")), rs.getTimestamp("installed_on"), rs.getString("installed_by"), toInteger((Number) rs.getObject("execution_time")), rs.getBoolean("success")); } }); } catch (SQLException e) { throw new FlywayException( "Error while retrieving the list of applied migrations from metadata table " + table, e); } }
/** * Calculates the installed rank for the new migration to be inserted. * * @return The installed rank. */ private int calculateInstalledRank() throws SQLException { int currentMax = jdbcTemplate.queryForInt( "SELECT MAX(" + dbSupport.quote("installed_rank") + ")" + " FROM " + table); return currentMax + 1; }
public void addAppliedMigration(AppliedMigration appliedMigration) { createIfNotExists(); MigrationVersion version = appliedMigration.getVersion(); try { int versionRank = calculateVersionRank(version); jdbcTemplate.update( "UPDATE " + table + " SET " + dbSupport.quote("version_rank") + " = " + dbSupport.quote("version_rank") + " + 1 WHERE " + dbSupport.quote("version_rank") + " >= ?", versionRank); jdbcTemplate.update( "INSERT INTO " + table + " (" + dbSupport.quote("version_rank") + "," + dbSupport.quote("installed_rank") + "," + dbSupport.quote("version") + "," + dbSupport.quote("description") + "," + dbSupport.quote("type") + "," + dbSupport.quote("script") + "," + dbSupport.quote("checksum") + "," + dbSupport.quote("installed_by") + "," + dbSupport.quote("execution_time") + "," + dbSupport.quote("success") + ")" + " VALUES (?, ?, ?, ?, ?, ?, ?, " + dbSupport.getCurrentUserFunction() + ", ?, ?)", versionRank, calculateInstalledRank(), version.toString(), appliedMigration.getDescription(), appliedMigration.getType().name(), appliedMigration.getScript(), appliedMigration.getChecksum(), appliedMigration.getExecutionTime(), appliedMigration.isSuccess()); LOG.debug("MetaData table " + table + " successfully updated to reflect changes"); } catch (SQLException e) { throw new FlywayException( "Unable to insert row for version '" + version + "' in metadata table " + table, e); } }
/** * Applies this migration to the database. The migration state and the execution time are updated * accordingly. * * @param migration The migration to apply. * @param isOutOfOrder If this migration is being applied out of order. * @return The result of the migration. */ private MigrationVersion applyMigration(final ResolvedMigration migration, boolean isOutOfOrder) { MigrationVersion version = migration.getVersion(); if (isOutOfOrder) { LOG.info("Migrating schema " + schema + " to version " + version + " (out of order)"); } else { LOG.info("Migrating schema " + schema + " to version " + version); } StopWatch stopWatch = new StopWatch(); stopWatch.start(); try { new TransactionTemplate(connectionUserObjects) .execute( new TransactionCallback<Void>() { public Void doInTransaction() { migration.getExecutor().execute(connectionUserObjects); return null; } }); LOG.debug( "Successfully completed and committed migration of schema " + schema + " to version " + version); } catch (FlywayException e) { String failedMsg = "Migration of schema " + schema + " to version " + version + " failed!"; if (dbSupport.supportsDdlTransactions()) { LOG.error(failedMsg + " Changes successfully rolled back."); } else { LOG.error(failedMsg + " Please restore backups and roll back database and code!"); stopWatch.stop(); int executionTime = (int) stopWatch.getTotalTimeMillis(); AppliedMigration appliedMigration = new AppliedMigration( version, migration.getDescription(), migration.getType(), migration.getScript(), migration.getChecksum(), executionTime, false); metaDataTable.addAppliedMigration(appliedMigration); } throw e; } stopWatch.stop(); int executionTime = (int) stopWatch.getTotalTimeMillis(); AppliedMigration appliedMigration = new AppliedMigration( version, migration.getDescription(), migration.getType(), migration.getScript(), migration.getChecksum(), executionTime, true); metaDataTable.addAppliedMigration(appliedMigration); return version; }