package org.javers.repository.sql.schema; import org.javers.repository.sql.ConnectionProvider; import org.polyjdbc.core.PolyJDBC; import org.polyjdbc.core.dialect.*; import org.polyjdbc.core.schema.SchemaInspector; import org.polyjdbc.core.schema.SchemaManager; import org.polyjdbc.core.schema.model.Schema; import org.polyjdbc.core.util.TheCloser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.sql.*; import java.util.Map; /** * @author bartosz walacik */ public class JaversSchemaManager extends SchemaNameAware { private static final Logger logger = LoggerFactory.getLogger(JaversSchemaManager.class); private SchemaInspector schemaInspector; private SchemaManager schemaManager; private final Dialect dialect; private final FixedSchemaFactory schemaFactory; private final PolyJDBC polyJDBC; private final ConnectionProvider connectionProvider; public JaversSchemaManager(Dialect dialect, FixedSchemaFactory schemaFactory, PolyJDBC polyJDBC, ConnectionProvider connectionProvider, TableNameProvider tableNameProvider) { super(tableNameProvider); this.dialect = dialect; this.schemaFactory = schemaFactory; this.polyJDBC = polyJDBC; this.connectionProvider = connectionProvider; } public void ensureSchema() { this.schemaInspector = polyJDBC.schemaInspector(); this.schemaManager = polyJDBC.schemaManager(); for (Map.Entry<String, Schema> e : schemaFactory.allTablesSchema(dialect).entrySet()) { ensureTable(e.getKey(), e.getValue()); } alterCommitIdColumnIfNeeded(); // JaVers 2.5 to 2.6 schema migration TheCloser.close(schemaManager, schemaInspector); } /** * JaVers 2.5 to 2.6 schema migration */ private void alterCommitIdColumnIfNeeded() { ColumnType commitIdColType = getTypeOf(getCommitTableNameWithSchema(), "commit_id"); if (commitIdColType.precision == 12) { logger.info("migrating db schema from JaVers 2.5 to 2.6 ..."); if (dialect instanceof PostgresDialect) { executeSQL("ALTER TABLE " + getCommitTableNameWithSchema() + " ALTER COLUMN commit_id TYPE numeric(22,2)"); } else if (dialect instanceof H2Dialect) { executeSQL("ALTER TABLE " + getCommitTableNameWithSchema() + " ALTER COLUMN commit_id numeric(22,2)"); } else if (dialect instanceof MysqlDialect) { executeSQL("ALTER TABLE " + getCommitTableNameWithSchema() + " MODIFY commit_id numeric(22,2)"); } else if (dialect instanceof OracleDialect) { executeSQL("ALTER TABLE " + getCommitTableNameWithSchema() + " MODIFY commit_id number(22,2)"); } else if (dialect instanceof MsSqlDialect) { executeSQL("drop index jv_commit_commit_id_idx on " + getCommitTableNameWithSchema()); executeSQL("ALTER TABLE " + getCommitTableNameWithSchema() + " ALTER COLUMN commit_id numeric(22,2)"); executeSQL("CREATE INDEX jv_commit_commit_id_idx ON " + getCommitTableNameWithSchema() + " (commit_id)"); } else { handleUnsupportedDialect(); } } } private void handleUnsupportedDialect() { logger.error("\nno DB schema migration script for {} :(\nplease contact with JaVers team, javers@javers.org", dialect.getCode()); } private boolean executeSQL(String sql) { try { Statement stmt = connectionProvider.getConnection().createStatement(); boolean b = stmt.execute(sql); stmt.close(); return b; } catch (SQLException e) { throw new RuntimeException(e); } } private int executeUpdate(String sql) { try { Statement stmt = connectionProvider.getConnection().createStatement(); int cnt = stmt.executeUpdate(sql); stmt.close(); return cnt; } catch (SQLException e) { throw new RuntimeException(e); } } private ColumnType getTypeOf(String tableName, String colName) { try { Statement stmt = connectionProvider.getConnection().createStatement(); ResultSet res = stmt.executeQuery("select " + colName + " from " + tableName + " where 1<0"); int colType = res.getMetaData().getColumnType(1); int colPrec = res.getMetaData().getPrecision(1); stmt.close(); res.close(); return new ColumnType(colType, colPrec); } catch (SQLException e) { throw new RuntimeException(e); } } private boolean columnExists(String tableName, String colName) { try { Statement stmt = connectionProvider.getConnection().createStatement(); ResultSet res = stmt.executeQuery("select * from " + tableName + " where 1<0"); ResultSetMetaData metaData = res.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { if (metaData.getColumnName(i).equalsIgnoreCase(colName)) { return true; } } res.close(); stmt.close(); return false; } catch (SQLException e) { throw new RuntimeException(e); } } private void ensureTable(String tableName, Schema schema) { if (schemaInspector.relationExists(tableName)) { return; } logger.info("creating javers table {} ...", tableName); schemaManager.create(schema); } private void addStringColumn(String tableName, String colName, int len) { logger.warn("column " + tableName + "." + colName + " not exists, running ALTER TABLE ..."); String sqlType = dialect.types().string(len); if (dialect instanceof OracleDialect || dialect instanceof MsSqlDialect) { executeSQL("ALTER TABLE " + tableName + " ADD " + colName + " " + sqlType); } else { executeSQL("ALTER TABLE " + tableName + " ADD COLUMN " + colName + " " + sqlType); } } private void addLongColumn(String tableName, String colName) { logger.warn("column " + tableName + "." + colName + " not exists, running ALTER TABLE ..."); String sqlType = dialect.types().bigint(0); if (dialect instanceof OracleDialect || dialect instanceof MsSqlDialect) { executeSQL("ALTER TABLE " + tableName + " ADD " + colName + " " + sqlType); } else { executeSQL("ALTER TABLE " + tableName + " ADD COLUMN " + colName + " " + sqlType); } } public void dropSchema() { throw new RuntimeException("not implemented"); } static class ColumnType { final int type; final int precision; ColumnType(int type, int precision) { this.type = type; this.precision = precision; } } }