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}