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