001package io.ebean.migration.runner;
002
003import java.sql.Connection;
004import java.sql.PreparedStatement;
005import java.sql.ResultSet;
006import java.sql.SQLException;
007import java.util.ArrayList;
008import java.util.List;
009
010/**
011 * Handle database platform specific locking on db migration table.
012 */
013public class MigrationPlatform {
014
015  private static final String BASE_SELECT_ID = "select id from ";
016  private static final String BASE_SELECT_ALL = "select id, mtype, mstatus, mversion, mcomment, mchecksum, run_on, run_by, run_time from ";
017
018  /**
019   * Standard row locking for db migration table.
020   */
021  String forUpdateSuffix = " order by id for update";
022
023  /**
024   * Lock the migration table. The base implementation uses row locking but lock table would be preferred when available.
025   */
026  void lockMigrationTable(String sqlTable, Connection connection) throws SQLException {
027
028    final String selectSql = sqlSelectForUpdate(sqlTable);
029
030    try (PreparedStatement query = connection.prepareStatement(selectSql)) {
031      try (ResultSet resultSet = query.executeQuery()) {
032        while (resultSet.next()) {
033          resultSet.getInt(1);
034        }
035      }
036    }
037  }
038
039  /**
040   * Read the existing migrations from the db migration table.
041   */
042  List<MigrationMetaRow> readExistingMigrations(String sqlTable, Connection connection) throws SQLException {
043
044    final String selectSql = sqlSelectForReading(sqlTable);
045
046    List<MigrationMetaRow> rows = new ArrayList<>();
047    try (PreparedStatement query = connection.prepareStatement(selectSql)) {
048      try (ResultSet resultSet = query.executeQuery()) {
049        while (resultSet.next()) {
050          rows.add(new MigrationMetaRow(resultSet));
051        }
052      }
053    }
054    return rows;
055  }
056
057  /**
058   * Return the SQL to lock the rows in db migration table with row locking.
059   */
060  String sqlSelectForUpdate(String table) {
061    return BASE_SELECT_ID + table + forUpdateSuffix;
062  }
063
064  /**
065   * Return the SQL to read the db migration table.
066   */
067  String sqlSelectForReading(String table) {
068    return BASE_SELECT_ALL + table + forUpdateSuffix;
069  }
070
071  public static class Postgres extends MigrationPlatform {
072
073    @Override
074    void lockMigrationTable(String sqlTable, Connection connection) throws SQLException {
075      try (PreparedStatement query = connection.prepareStatement("lock table " + sqlTable)) {
076        query.execute();
077      }
078    }
079  }
080
081  public static class SqlServer extends MigrationPlatform {
082
083    public SqlServer() {
084      this.forUpdateSuffix = " with (updlock) order by id";
085    }
086  }
087
088  public static class NoLocking extends MigrationPlatform {
089
090    public NoLocking() {
091      this.forUpdateSuffix = " order by id";
092    }
093
094    @Override
095    void lockMigrationTable(String sqlTable, Connection connection) {
096      // do nothing
097    }
098  }
099}