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}