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