Java tutorial
package org.apache.ddlutils.alteration; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.ddlutils.PlatformInfo; import org.apache.ddlutils.model.CloneHelper; import org.apache.ddlutils.model.Column; import org.apache.ddlutils.model.Database; import org.apache.ddlutils.model.ForeignKey; import org.apache.ddlutils.model.Index; import org.apache.ddlutils.model.Table; import org.apache.ddlutils.util.StringUtilsExt; /** * Compares two database models and creates change objects that express how to * adapt the first model so that it becomes the second one. Neither of the models * are changed in the process, however, it is also assumed that the models do not * change in between. * * @version $Revision: $ */ public class ModelComparator { /** The log for this comparator. */ private final Log _log = LogFactory.getLog(ModelComparator.class); /** The platform information. */ private PlatformInfo _platformInfo; /** The predicate that defines which changes are supported by the platform. */ private TableDefinitionChangesPredicate _tableDefCangePredicate; /** The object clone helper. */ private CloneHelper _cloneHelper = new CloneHelper(); /** Whether comparison is case sensitive. */ private boolean _caseSensitive; /** Whether the comparator should generate {@link PrimaryKeyChange} objects. */ private boolean _generatePrimaryKeyChanges = true; /** Whether {@link RemoveColumnChange} objects for primary key columns are enough or additional primary key change objects are necessary. */ private boolean _canDropPrimaryKeyColumns = true; /** * Creates a new model comparator object. * * @param platformInfo The platform info * @param tableDefChangePredicate The predicate that defines whether tables changes are supported * by the platform or not; all changes are supported if this is null * @param caseSensitive Whether comparison is case sensitive */ public ModelComparator(PlatformInfo platformInfo, TableDefinitionChangesPredicate tableDefChangePredicate, boolean caseSensitive) { _platformInfo = platformInfo; _caseSensitive = caseSensitive; _tableDefCangePredicate = tableDefChangePredicate; } /** * Specifies whether the comparator should generate {@link PrimaryKeyChange} objects or a * pair of {@link RemovePrimaryKeyChange} and {@link AddPrimaryKeyChange} objects instead. * The default value is <code>true</code>. * * @param generatePrimaryKeyChanges Whether to create {@link PrimaryKeyChange} objects */ public void setGeneratePrimaryKeyChanges(boolean generatePrimaryKeyChanges) { _generatePrimaryKeyChanges = generatePrimaryKeyChanges; } /** * Specifies whether the {@link RemoveColumnChange} are fine even for primary key columns. * If the platform cannot drop primary key columns, set this to <code>false</code> and the * comparator will create additional primary key changes. * The default value is <code>true</code>. * * @param canDropPrimaryKeyColumns Whether {@link RemoveColumnChange} objecs for primary * key columns are ok */ public void setCanDropPrimaryKeyColumns(boolean canDropPrimaryKeyColumns) { _canDropPrimaryKeyColumns = canDropPrimaryKeyColumns; } /** * Returns the info object for the platform. * * @return The platform info object */ protected PlatformInfo getPlatformInfo() { return _platformInfo; } /** * Determines whether comparison should be case sensitive. * * @return <code>true</code> if case matters */ protected boolean isCaseSensitive() { return _caseSensitive; } /** * Compares the two models and returns the changes necessary to create the second * model from the first one. * * @param sourceModel The source model * @param targetModel The target model * @return The changes */ public List compare(Database sourceModel, Database targetModel) { Database intermediateModel = _cloneHelper.clone(sourceModel); return compareModels(sourceModel, intermediateModel, targetModel); } /** * Compares the given source and target models and creates change objects to get from * the source to the target one. These changes will be applied to the given * intermediate model (the other two won't be changed), so that it will be equal to * the target model after this model has finished. * * @param sourceModel The source model * @param intermediateModel The intermediate model to apply the changes to * @param targetModel The target model * @return The changes */ protected List compareModels(Database sourceModel, Database intermediateModel, Database targetModel) { ArrayList changes = new ArrayList(); changes.addAll(checkForRemovedForeignKeys(sourceModel, intermediateModel, targetModel)); changes.addAll(checkForRemovedTables(sourceModel, intermediateModel, targetModel)); for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++) { Table intermediateTable = intermediateModel.getTable(tableIdx); Table sourceTable = sourceModel.findTable(intermediateTable.getName(), _caseSensitive); Table targetTable = targetModel.findTable(intermediateTable.getName(), _caseSensitive); List tableChanges = compareTables(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable); changes.addAll(tableChanges); } changes.addAll(checkForAddedTables(sourceModel, intermediateModel, targetModel)); changes.addAll(checkForAddedForeignKeys(sourceModel, intermediateModel, targetModel)); return changes; } /** * Creates change objects for foreign keys that are present in the given source model but are no longer in the target * model, and applies them to the given intermediate model. * * @param sourceModel The source model * @param intermediateModel The intermediate model to apply the changes to * @param targetModel The target model * @return The changes */ protected List checkForRemovedForeignKeys(Database sourceModel, Database intermediateModel, Database targetModel) { List changes = new ArrayList(); for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++) { Table intermediateTable = intermediateModel.getTable(tableIdx); Table targetTable = targetModel.findTable(intermediateTable.getName(), _caseSensitive); ForeignKey[] intermediateFks = intermediateTable.getForeignKeys(); // Dropping foreign keys from tables to be removed might not be necessary, but some databases might require it for (int fkIdx = 0; fkIdx < intermediateFks.length; fkIdx++) { ForeignKey sourceFk = intermediateFks[fkIdx]; ForeignKey targetFk = targetTable == null ? null : findCorrespondingForeignKey(targetTable, sourceFk); if (targetFk == null) { if (_log.isInfoEnabled()) { _log.info("Foreign key " + sourceFk + " needs to be removed from table " + intermediateTable.getName()); } RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(intermediateTable.getName(), sourceFk); changes.add(fkChange); fkChange.apply(intermediateModel, _caseSensitive); } } } return changes; } /** * Creates change objects for foreign keys that are not present in the given source model but are in the target * model, and applies them to the given intermediate model. * * @param sourceModel The source model * @param intermediateModel The intermediate model to apply the changes to * @param targetModel The target model * @return The changes */ protected List checkForAddedForeignKeys(Database sourceModel, Database intermediateModel, Database targetModel) { List changes = new ArrayList(); for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++) { Table targetTable = targetModel.getTable(tableIdx); Table intermediateTable = intermediateModel.findTable(targetTable.getName(), _caseSensitive); for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++) { ForeignKey targetFk = targetTable.getForeignKey(fkIdx); ForeignKey intermediateFk = findCorrespondingForeignKey(intermediateTable, targetFk); if (intermediateFk == null) { if (_log.isInfoEnabled()) { _log.info("Foreign key " + targetFk + " needs to be added to table " + intermediateTable.getName()); } intermediateFk = _cloneHelper.clone(targetFk, intermediateTable, intermediateModel, _caseSensitive); AddForeignKeyChange fkChange = new AddForeignKeyChange(intermediateTable.getName(), intermediateFk); changes.add(fkChange); fkChange.apply(intermediateModel, _caseSensitive); } } } return changes; } /** * Creates change objects for tables that are present in the given source model but are no longer in the target * model, and applies them to the given intermediate model. * * @param sourceModel The source model * @param intermediateModel The intermediate model to apply the changes to * @param targetModel The target model * @return The changes */ protected List checkForRemovedTables(Database sourceModel, Database intermediateModel, Database targetModel) { List changes = new ArrayList(); Table[] intermediateTables = intermediateModel.getTables(); for (int tableIdx = 0; tableIdx < intermediateTables.length; tableIdx++) { Table intermediateTable = intermediateTables[tableIdx]; Table targetTable = targetModel.findTable(intermediateTable.getName(), _caseSensitive); if (targetTable == null) { if (_log.isInfoEnabled()) { _log.info("Table " + intermediateTable.getName() + " needs to be removed"); } RemoveTableChange tableChange = new RemoveTableChange(intermediateTable.getName()); changes.add(tableChange); tableChange.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Creates change objects for tables that are not present in the given source model but are in the target * model, and applies them to the given intermediate model. * * @param sourceModel The source model * @param intermediateModel The intermediate model to apply the changes to * @param targetModel The target model * @return The changes */ protected List checkForAddedTables(Database sourceModel, Database intermediateModel, Database targetModel) { List changes = new ArrayList(); for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++) { Table targetTable = targetModel.getTable(tableIdx); Table intermediateTable = intermediateModel.findTable(targetTable.getName(), _caseSensitive); if (intermediateTable == null) { if (_log.isInfoEnabled()) { _log.info("Table " + targetTable.getName() + " needs to be added"); } // we're using a clone of the target table, and remove all foreign // keys as these will be added later intermediateTable = _cloneHelper.clone(targetTable, true, false, intermediateModel, _caseSensitive); AddTableChange tableChange = new AddTableChange(intermediateTable); changes.add(tableChange); tableChange.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Compares the two tables and returns the changes necessary to create the second * table from the first one. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to which the changes will be applied incrementally * @param intermediateTable The table corresponding to the source table in the intermediate model * @param targetModel The target model which contains the target table * @param targetTable The target table * @return The changes */ protected List compareTables(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { ArrayList changes = new ArrayList(); changes.addAll(checkForRemovedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); ArrayList tableDefinitionChanges = new ArrayList(); Table tmpTable = _cloneHelper.clone(intermediateTable, true, false, intermediateModel, _caseSensitive); tableDefinitionChanges.addAll(checkForRemovedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); tableDefinitionChanges.addAll(checkForChangeOfColumnOrder(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); tableDefinitionChanges.addAll(checkForChangedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); tableDefinitionChanges.addAll(checkForAddedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); tableDefinitionChanges.addAll(checkForPrimaryKeyChanges(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); // TOOD: check for foreign key changes (on delete/on update) if (!tableDefinitionChanges.isEmpty()) { if ((_tableDefCangePredicate == null) || _tableDefCangePredicate.areSupported(tmpTable, tableDefinitionChanges)) { changes.addAll(tableDefinitionChanges); } else { // we need to recreate the table; for this to work we need to remove foreign keys to and from the table // however, we don't have to add them back here as there is a check for added foreign keys/indexes // later on anyways // we also don't have to drop indexes on the original table ForeignKey[] fks = intermediateTable.getForeignKeys(); for (int fkIdx = 0; fkIdx < fks.length; fkIdx++) { RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(intermediateTable.getName(), fks[fkIdx]); changes.add(fkChange); fkChange.apply(intermediateModel, _caseSensitive); } for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++) { Table curTable = intermediateModel.getTable(tableIdx); if (curTable != intermediateTable) { ForeignKey[] curFks = curTable.getForeignKeys(); for (int fkIdx = 0; fkIdx < curFks.length; fkIdx++) { if ((_caseSensitive && curFks[fkIdx].getForeignTableName().equals(intermediateTable.getName())) || (!_caseSensitive && curFks[fkIdx].getForeignTableName() .equalsIgnoreCase(intermediateTable.getName()))) { RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(curTable.getName(), curFks[fkIdx]); changes.add(fkChange); fkChange.apply(intermediateModel, _caseSensitive); } } } } RecreateTableChange tableChange = new RecreateTableChange(intermediateTable.getName(), intermediateTable, new ArrayList(tableDefinitionChanges)); changes.add(tableChange); tableChange.apply(intermediateModel, _caseSensitive); } } changes.addAll(checkForAddedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable)); return changes; } /** * Returns the names of the columns in the intermediate table corresponding to the given column objects. * * @param columns The column objects * @param intermediateTable The intermediate table * @return The column names */ protected String[] getIntermediateColumnNamesFor(Column[] columns, Table intermediateTable) { String[] result = new String[columns.length]; for (int idx = 0; idx < columns.length; idx++) { result[idx] = intermediateTable.findColumn(columns[idx].getName(), _caseSensitive).getName(); } return result; } /** * Creates change objects for indexes that are present in the given source table but are no longer in the target * table, and applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForRemovedIndexes(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); Index[] indexes = intermediateTable.getIndices(); for (int indexIdx = 0; indexIdx < indexes.length; indexIdx++) { Index sourceIndex = indexes[indexIdx]; Index targetIndex = findCorrespondingIndex(targetTable, sourceIndex); if (targetIndex == null) { if (_log.isInfoEnabled()) { _log.info("Index " + sourceIndex.getName() + " needs to be removed from table " + intermediateTable.getName()); } RemoveIndexChange change = new RemoveIndexChange(intermediateTable.getName(), sourceIndex); changes.add(change); change.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Creates change objects for indexes that are not present in the given source table but are in the target * table, and applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForAddedIndexes(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); for (int indexIdx = 0; indexIdx < targetTable.getIndexCount(); indexIdx++) { Index targetIndex = targetTable.getIndex(indexIdx); Index intermediateIndex = findCorrespondingIndex(intermediateTable, targetIndex); Index sourceIndex = findCorrespondingIndex(sourceTable, targetIndex); if ((sourceIndex == null) && (intermediateIndex == null)) { if (_log.isInfoEnabled()) { _log.info("Index " + targetIndex.getName() + " needs to be created for table " + intermediateTable.getName()); } Index clonedIndex = _cloneHelper.clone(targetIndex, intermediateTable, _caseSensitive); AddIndexChange change = new AddIndexChange(intermediateTable.getName(), clonedIndex); changes.add(change); change.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Checks for changes in the column order between the given source and target table, creates change objects for these and * applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForChangeOfColumnOrder(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); List targetOrder = new ArrayList(); int numChangedPKs = 0; for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++) { Column targetColumn = targetTable.getColumn(columnIdx); Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive); if (sourceColumn != null) { targetOrder.add(sourceColumn); } } HashMap newPositions = new HashMap(); for (int columnIdx = 0; columnIdx < intermediateTable.getColumnCount(); columnIdx++) { Column sourceColumn = intermediateTable.getColumn(columnIdx); int targetIdx = targetOrder.indexOf(sourceColumn); if ((targetIdx >= 0) && (targetIdx != columnIdx)) { newPositions.put(sourceColumn.getName(), new Integer(targetIdx)); if (sourceColumn.isPrimaryKey()) { numChangedPKs++; } } } if (!newPositions.isEmpty()) { ColumnOrderChange change = new ColumnOrderChange(intermediateTable.getName(), newPositions); change.apply(intermediateModel, _caseSensitive); if (numChangedPKs > 1) { // create pk change that only covers the order change // fortunately, the order change will have adjusted the pk order already changes.add(new PrimaryKeyChange(intermediateTable.getName(), getIntermediateColumnNamesFor( intermediateTable.getPrimaryKeyColumns(), intermediateTable))); } changes.add(change); } return changes; } /** * Creates change objects for columns that are present in the given source table but are no longer in the target * table, and applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForRemovedColumns(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { // if the platform does not support dropping pk columns, then the pk handling above will // generate appropriate pk changes List changes = new ArrayList(); Column[] columns = intermediateTable.getColumns(); for (int columnIdx = 0; columnIdx < columns.length; columnIdx++) { Column sourceColumn = columns[columnIdx]; Column targetColumn = targetTable.findColumn(sourceColumn.getName(), _caseSensitive); if (targetColumn == null) { if (_log.isInfoEnabled()) { _log.info("Column " + sourceColumn.getName() + " needs to be removed from table " + intermediateTable.getName()); } RemoveColumnChange change = new RemoveColumnChange(intermediateTable.getName(), sourceColumn.getName()); changes.add(change); change.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Creates change objects for columns that are not present in the given source table but are in the target * table, and applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForAddedColumns(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++) { Column targetColumn = targetTable.getColumn(columnIdx); Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive); if (sourceColumn == null) { String prevColumn = (columnIdx > 0 ? intermediateTable.getColumn(columnIdx - 1).getName() : null); String nextColumn = (columnIdx < intermediateTable.getColumnCount() ? intermediateTable.getColumn(columnIdx).getName() : null); Column clonedColumn = _cloneHelper.clone(targetColumn, false); AddColumnChange change = new AddColumnChange(intermediateTable.getName(), clonedColumn, prevColumn, nextColumn); changes.add(change); change.apply(intermediateModel, _caseSensitive); } } return changes; } /** * Creates change objects for columns that have a different in the given source and target table, and applies them * to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForChangedColumns(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++) { Column targetColumn = targetTable.getColumn(columnIdx); Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive); if (sourceColumn != null) { ColumnDefinitionChange change = compareColumns(intermediateTable, sourceColumn, targetTable, targetColumn); if (change != null) { changes.add(change); change.apply(intermediateModel, _caseSensitive); } } } return changes; } /** * Creates change objects for primary key differences (primary key added/removed/changed), and applies them to the given intermediate model. * * @param sourceModel The source model * @param sourceTable The source table * @param intermediateModel The intermediate model to apply the changes to * @param intermediateTable The table from the intermediate model corresponding to the source table * @param targetModel The target model * @param targetTable The target table * @return The changes */ protected List checkForPrimaryKeyChanges(Database sourceModel, Table sourceTable, Database intermediateModel, Table intermediateTable, Database targetModel, Table targetTable) { List changes = new ArrayList(); Column[] sourcePK = sourceTable.getPrimaryKeyColumns(); Column[] curPK = intermediateTable.getPrimaryKeyColumns(); Column[] targetPK = targetTable.getPrimaryKeyColumns(); if ((curPK.length == 0) && (targetPK.length > 0)) { if (_log.isInfoEnabled()) { _log.info("A primary key needs to be added to the table " + intermediateTable.getName()); } AddPrimaryKeyChange change = new AddPrimaryKeyChange(intermediateTable.getName(), getIntermediateColumnNamesFor(targetPK, intermediateTable)); changes.add(change); change.apply(intermediateModel, _caseSensitive); } else if ((targetPK.length == 0) && (curPK.length > 0)) { if (_log.isInfoEnabled()) { _log.info("The primary key needs to be removed from the table " + intermediateTable.getName()); } RemovePrimaryKeyChange change = new RemovePrimaryKeyChange(intermediateTable.getName()); changes.add(change); change.apply(intermediateModel, _caseSensitive); } else { boolean changePK = false; if ((curPK.length != targetPK.length) || (!_canDropPrimaryKeyColumns && sourcePK.length > targetPK.length)) { changePK = true; } else if ((curPK.length > 0) && (targetPK.length > 0)) { for (int pkColumnIdx = 0; (pkColumnIdx < curPK.length) && !changePK; pkColumnIdx++) { if (!StringUtilsExt.equals(curPK[pkColumnIdx].getName(), targetPK[pkColumnIdx].getName(), _caseSensitive)) { changePK = true; } } } if (changePK) { if (_log.isInfoEnabled()) { _log.info("The primary key of table " + intermediateTable.getName() + " needs to be changed"); } if (_generatePrimaryKeyChanges) { PrimaryKeyChange change = new PrimaryKeyChange(intermediateTable.getName(), getIntermediateColumnNamesFor(targetPK, intermediateTable)); changes.add(change); change.apply(intermediateModel, changePK); } else { RemovePrimaryKeyChange removePKChange = new RemovePrimaryKeyChange(intermediateTable.getName()); AddPrimaryKeyChange addPKChange = new AddPrimaryKeyChange(intermediateTable.getName(), getIntermediateColumnNamesFor(targetPK, intermediateTable)); changes.add(removePKChange); changes.add(addPKChange); removePKChange.apply(intermediateModel, _caseSensitive); addPKChange.apply(intermediateModel, _caseSensitive); } } } return changes; } /** * Compares the two columns and returns the change necessary to create the second * column from the first one if they differe. * * @param sourceTable The source table which contains the source column * @param sourceColumn The source column * @param targetTable The target table which contains the target column * @param targetColumn The target column * @return The change or <code>null</code> if the columns are the same */ protected ColumnDefinitionChange compareColumns(Table sourceTable, Column sourceColumn, Table targetTable, Column targetColumn) { if (ColumnDefinitionChange.isChanged(getPlatformInfo(), sourceColumn, targetColumn)) { Column newColumnDef = _cloneHelper.clone(sourceColumn, true); int targetTypeCode = _platformInfo.getTargetJdbcType(targetColumn.getTypeCode()); boolean sizeMatters = _platformInfo.hasSize(targetTypeCode); boolean scaleMatters = _platformInfo.hasPrecisionAndScale(targetTypeCode); newColumnDef.setTypeCode(targetColumn.getTypeCode()); newColumnDef.setSize(sizeMatters || scaleMatters ? targetColumn.getSize() : null); newColumnDef.setAutoIncrement(targetColumn.isAutoIncrement()); newColumnDef.setRequired(targetColumn.isRequired()); newColumnDef.setDescription(targetColumn.getDescription()); newColumnDef.setDefaultValue(targetColumn.getDefaultValue()); return new ColumnDefinitionChange(sourceTable.getName(), sourceColumn.getName(), newColumnDef); } else { return null; } } /** * Searches in the given table for a corresponding foreign key. If the given key * has no name, then a foreign key to the same table with the same columns (but not * necessarily in the same order) is searched. If the given key has a name, then the * corresponding key also needs to have the same name, or no name at all, but not a * different one. * * @param table The table to search in * @param fk The original foreign key * @return The corresponding foreign key if found */ protected ForeignKey findCorrespondingForeignKey(Table table, ForeignKey fk) { for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++) { ForeignKey curFk = table.getForeignKey(fkIdx); if ((_caseSensitive && fk.equals(curFk)) || (!_caseSensitive && fk.equalsIgnoreCase(curFk))) { return curFk; } } return null; } /** * Searches in the given table for a corresponding index. If the given index * has no name, then a index to the same table with the same columns in the * same order is searched. If the given index has a name, then the a corresponding * index also needs to have the same name, or no name at all, but not a different one. * * @param table The table to search in * @param index The original index * @return The corresponding index if found */ protected Index findCorrespondingIndex(Table table, Index index) { for (int indexIdx = 0; indexIdx < table.getIndexCount(); indexIdx++) { Index curIndex = table.getIndex(indexIdx); if ((_caseSensitive && index.equals(curIndex)) || (!_caseSensitive && index.equalsIgnoreCase(curIndex))) { return curIndex; } } return null; } }