package io.ebean.dbmigration.model; import io.ebean.dbmigration.migration.AddColumn; import io.ebean.dbmigration.migration.AddHistoryTable; import io.ebean.dbmigration.migration.AlterColumn; import io.ebean.dbmigration.migration.ChangeSet; import io.ebean.dbmigration.migration.ChangeSetType; import io.ebean.dbmigration.migration.CreateIndex; import io.ebean.dbmigration.migration.CreateTable; import io.ebean.dbmigration.migration.DropColumn; import io.ebean.dbmigration.migration.DropHistoryTable; import io.ebean.dbmigration.migration.DropIndex; import io.ebean.dbmigration.migration.DropTable; import io.ebean.dbmigration.migration.Migration; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; /** * Holds all the tables, views, indexes etc that represent the model. * <p> * Migration changeSets can be applied to the model. * </p> */ public class ModelContainer { /** * All the tables in the model. */ private final Map<String, MTable> tables = new LinkedHashMap<>(); /** * All the non unique non foreign key indexes. */ private final Map<String, MIndex> indexes = new LinkedHashMap<>(); private final PendingDrops pendingDrops = new PendingDrops(); public ModelContainer() { } /** * Adjust the FK references on all the draft tables. */ public void adjustDraftReferences() { for (MTable table : this.tables.values()) { if (table.isDraft()) { table.adjustReferences(this); } } } /** * Return the map of all the tables. */ public Map<String, MTable> getTables() { return tables; } /** * Return the map of all the non unique non fk indexes. */ public Map<String, MIndex> getIndexes() { return indexes; } /** * Return the table by name. */ public MTable getTable(String tableName) { return tables.get(tableName); } /** * Return the index by name. */ public MIndex getIndex(String indexName) { return indexes.get(indexName); } /** * Apply a migration with associated changeSets to the model. */ public void apply(Migration migration, MigrationVersion version) { List<ChangeSet> changeSets = migration.getChangeSet(); for (ChangeSet changeSet : changeSets) { boolean pending = changeSet.getType() == ChangeSetType.PENDING_DROPS; if (pending) { // un-applied drop columns etc pendingDrops.add(version, changeSet); } else if (isDropsFor(changeSet)) { pendingDrops.appliedDropsFor(changeSet); } if (!isDropsFor(changeSet)) { applyChangeSet(changeSet); } } } /** * Return true if the changeSet contains drops for a previous PENDING_DROPS changeSet. */ private boolean isDropsFor(ChangeSet changeSet) { return changeSet.getDropsFor() != null; } /** * Apply a changeSet to the model. */ protected void applyChangeSet(ChangeSet changeSet) { List<Object> changeSetChildren = changeSet.getChangeSetChildren(); for (Object change : changeSetChildren) { if (change instanceof CreateTable) { applyChange((CreateTable) change); } else if (change instanceof DropTable) { applyChange((DropTable) change); } else if (change instanceof AlterColumn) { applyChange((AlterColumn) change); } else if (change instanceof AddColumn) { applyChange((AddColumn) change); } else if (change instanceof DropColumn) { applyChange((DropColumn) change); } else if (change instanceof CreateIndex) { applyChange((CreateIndex) change); } else if (change instanceof DropIndex) { applyChange((DropIndex) change); } else if (change instanceof AddHistoryTable) { applyChange((AddHistoryTable) change); } else if (change instanceof DropHistoryTable) { applyChange((DropHistoryTable) change); } } } /** * Set the withHistory flag on the associated base table. */ private void applyChange(AddHistoryTable change) { MTable table = tables.get(change.getBaseTable()); if (table == null) { throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?"); } table.setWithHistory(true); } /** * Unset the withHistory flag on the associated base table. */ private void applyChange(DropHistoryTable change) { MTable table = tables.get(change.getBaseTable()); if (table == null) { throw new IllegalStateException("Table [" + change.getBaseTable() + "] does not exist in model?"); } table.setWithHistory(false); } /** * Apply a CreateTable change to the model. */ protected void applyChange(CreateTable createTable) { String tableName = createTable.getName(); if (tables.containsKey(tableName)) { throw new IllegalStateException("Table [" + tableName + "] already exists in model?"); } MTable table = new MTable(createTable); tables.put(tableName, table); } /** * Apply a DropTable change to the model. */ protected void applyChange(DropTable dropTable) { String tableName = dropTable.getName(); if (!tables.containsKey(tableName)) { throw new IllegalStateException("Table [" + tableName + "] does not exists in model?"); } tables.remove(tableName); } /** * Apply a CreateTable change to the model. */ protected void applyChange(CreateIndex createIndex) { String indexName = createIndex.getIndexName(); if (indexes.containsKey(indexName)) { throw new IllegalStateException("Index [" + indexName + "] already exists in model?"); } MIndex index = new MIndex(createIndex); indexes.put(createIndex.getIndexName(), index); } /** * Apply a DropTable change to the model. */ protected void applyChange(DropIndex dropIndex) { String name = dropIndex.getIndexName(); if (!indexes.containsKey(name)) { throw new IllegalStateException("Index [" + name + "] does not exist in model?"); } indexes.remove(name); } /** * Apply a AddColumn change to the model. */ protected void applyChange(AddColumn addColumn) { MTable table = tables.get(addColumn.getTableName()); if (table == null) { throw new IllegalStateException("Table [" + addColumn.getTableName() + "] does not exist in model?"); } table.apply(addColumn); } /** * Apply a AddColumn change to the model. */ protected void applyChange(AlterColumn alterColumn) { MTable table = tables.get(alterColumn.getTableName()); if (table == null) { throw new IllegalStateException("Table [" + alterColumn.getTableName() + "] does not exist in model?"); } table.apply(alterColumn); } /** * Apply a DropColumn change to the model. */ protected void applyChange(DropColumn dropColumn) { MTable table = tables.get(dropColumn.getTableName()); if (table == null) { throw new IllegalStateException("Table [" + dropColumn.getTableName() + "] does not exist in model?"); } table.apply(dropColumn); } /** * Add a table (typically from reading EbeanServer meta data). */ public MTable addTable(MTable table) { return tables.put(table.getName(), table); } /** * Add a single column index. */ public void addIndex(String indexName, String tableName, String columnName) { indexes.put(indexName, new MIndex(indexName, tableName, columnName)); } /** * Add a multi column index. */ public void addIndex(String indexName, String tableName, String[] columnNames) { indexes.put(indexName, new MIndex(indexName, tableName, columnNames)); } /** * Return the list of versions containing un-applied pending drops. */ public List<String> getPendingDrops() { return pendingDrops.pendingDrops(); } /** * Return the migration for the pending drops for a given version. */ public Migration migrationForPendingDrop(String pendingVersion) { return pendingDrops.migrationForVersion(pendingVersion); } /** * Register the drop columns on history tables that have not been applied yet. */ public void registerPendingHistoryDropColumns(ModelContainer newModel) { pendingDrops.registerPendingHistoryDropColumns(newModel); } /** * Register any pending drop columns on history tables. These columns are now not in the current * logical model but we still need to include them in the history views and triggers until they * are actually dropped. */ public void registerPendingHistoryDropColumns(ChangeSet changeSet) { for (Object change : changeSet.getChangeSetChildren()) { if (change instanceof DropColumn) { DropColumn dropColumn = (DropColumn) change; if (Boolean.TRUE.equals(dropColumn.isWithHistory())) { registerPendingDropColumn(dropColumn); } } } } /** * Register a drop column on a history tables that has not been applied yet. */ private void registerPendingDropColumn(DropColumn dropColumn) { MTable table = getTable(dropColumn.getTableName()); if (table == null) { throw new IllegalArgumentException("Table [" + dropColumn.getTableName() + "] not found?"); } table.registerPendingDropColumn(dropColumn.getColumnName()); } }