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}