Return-Path: Delivered-To: apmail-db-ddlutils-dev-archive@www.apache.org Received: (qmail 72668 invoked from network); 10 Dec 2007 08:23:11 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.2) by minotaur.apache.org with SMTP; 10 Dec 2007 08:23:11 -0000 Received: (qmail 95850 invoked by uid 500); 10 Dec 2007 08:22:59 -0000 Delivered-To: apmail-db-ddlutils-dev-archive@db.apache.org Received: (qmail 95822 invoked by uid 500); 10 Dec 2007 08:22:59 -0000 Mailing-List: contact ddlutils-dev-help@db.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: ddlutils-dev@db.apache.org Delivered-To: mailing list ddlutils-dev@db.apache.org Received: (qmail 95811 invoked by uid 500); 10 Dec 2007 08:22:59 -0000 Delivered-To: apmail-db-ddlutils-commits@db.apache.org Received: (qmail 95808 invoked by uid 99); 10 Dec 2007 08:22:59 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 10 Dec 2007 00:22:59 -0800 X-ASF-Spam-Status: No, hits=-100.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.3] (HELO eris.apache.org) (140.211.11.3) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 10 Dec 2007 08:22:44 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 4FD3A1A9842; Mon, 10 Dec 2007 00:22:47 -0800 (PST) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r602807 [3/15] - in /db/ddlutils/trunk: ./ src/java/org/apache/ddlutils/ src/java/org/apache/ddlutils/alteration/ src/java/org/apache/ddlutils/model/ src/java/org/apache/ddlutils/platform/ src/java/org/apache/ddlutils/platform/axion/ src/ja... Date: Mon, 10 Dec 2007 08:21:39 -0000 To: ddlutils-commits@db.apache.org From: tomdz@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20071210082247.4FD3A1A9842@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/ForeignKey.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/ForeignKey.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/ForeignKey.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/ForeignKey.java Mon Dec 10 00:20:47 2007 @@ -19,20 +19,24 @@ * under the License. */ +import java.io.Serializable; import java.util.HashSet; import java.util.Iterator; import org.apache.commons.collections.set.ListOrderedSet; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.ddlutils.util.StringUtils; /** * Represents a database foreign key. * * @version $Revision$ */ -public class ForeignKey implements Cloneable +public class ForeignKey implements Serializable { + /** Unique ID for serialization purposes. */ + private static final long serialVersionUID = 7833254626253719913L; /** The name of the foreign key, may be null. */ private String _name; /** The target table. */ @@ -292,6 +296,28 @@ } /** + * Determines whether this foreign key uses the indicated column as a local + * column in a reference. This method assumes that the caller checked + * already that the column is a column in the table owning this foreign key. + * + * @param columnName The name of the column to check + * @param caseSensitive Whether case matters when checking for the column's name + * @return true if a reference uses the column as a local + * column + */ + public boolean hasLocalColumn(String columnName, boolean caseSensitive) + { + for (int idx = 0; idx < getReferenceCount(); idx++) + { + if (StringUtils.equals(columnName, getReference(idx).getLocalColumnName(), caseSensitive)) + { + return true; + } + } + return false; + } + + /** * Determines whether this foreign key uses the given column as a foreign * column in a reference. * @@ -310,9 +336,32 @@ } return false; } + + /** + * Determines whether this foreign key uses the given column as a foreign + * column in a reference. This method assumes that the caller already checked + * whether this foreign key references the tale owning the indicate column. + * + * @param columnName The name of the column to check + * @param caseSensitive Whether case matters when checking for the column's name + * @return true if a reference uses the column as a foreign + * column + */ + public boolean hasForeignColumn(String columnName, boolean caseSensitive) + { + for (int idx = 0; idx < getReferenceCount(); idx++) + { + if (StringUtils.equals(columnName, getReference(idx).getForeignColumnName(), caseSensitive)) + { + return true; + } + } + return false; + } /** - * Determines whether this foreign key has an auto-generated associated index. + * Determines whether this foreign key has an auto-generated associated index. Note that + * this is a hint for the platform and has no relevancy to the model itself. * * @return true if an auto-generated index exists */ @@ -322,32 +371,14 @@ } /** - * Specifies whether this foreign key has an auto-generated associated index. + * Specifies whether this foreign key has an auto-generated associated index. Note that + * this is a hint set by the model reader and has no relevancy to the model itself. * * @param autoIndexPresent true if an auto-generated index exists */ public void setAutoIndexPresent(boolean autoIndexPresent) { _autoIndexPresent = autoIndexPresent; - } - - /** - * {@inheritDoc} - */ - public Object clone() throws CloneNotSupportedException - { - ForeignKey result = (ForeignKey)super.clone(); - - result._name = _name; - result._foreignTableName = _foreignTableName; - result._references = new ListOrderedSet(); - - for (Iterator it = _references.iterator(); it.hasNext();) - { - result._references.add(((Reference)it.next()).clone()); - } - - return result; } /** Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Index.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Index.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Index.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Index.java Mon Dec 10 00:20:47 2007 @@ -80,6 +80,15 @@ public boolean hasColumn(Column column); /** + * Determines whether this index includes the indicated column. + * + * @param columnName The name of the column to check for + * @param caseSensitive Whether the case of the column name matters for the check + * @return true if the column is included in this index + */ + public boolean hasColumn(String columnName, boolean caseSensitive); + + /** * Adds a column that makes up this index. * * @param column The column to add @@ -107,6 +116,14 @@ * @throws CloneNotSupportedException If the cloning did fail */ public Object clone() throws CloneNotSupportedException; + + /** + * Returns a clone of this index object. This is essentially the same method as + * {@link #clone()}, except that it does not throw a checked exception. + * + * @return The clone + */ + public Index getClone() throws ModelException; /** * Compares this index to the given one while ignoring the case of identifiers. Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexColumn.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexColumn.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexColumn.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexColumn.java Mon Dec 10 00:20:47 2007 @@ -29,7 +29,7 @@ * * @version $Revision$ */ -public class IndexColumn implements Cloneable, Serializable +public class IndexColumn implements Serializable { /** Unique ID for serialization purposes. */ private static final long serialVersionUID = -5009366896427504739L; @@ -43,8 +43,6 @@ /** The size of the column in the index. */ protected String _size; - // TODO: It might be useful if the referenced column is directly acessible here ? - /** * Creates a new index column object. */ @@ -154,19 +152,7 @@ { _size = size; } - - /** - * {@inheritDoc} - */ - public Object clone() throws CloneNotSupportedException - { - IndexColumn result = (IndexColumn)super.clone(); - - result._name = _name; - result._size = _size; - return result; - } - + /** * {@inheritDoc} */ Added: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexImplBase.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexImplBase.java?rev=602807&view=auto ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexImplBase.java (added) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/IndexImplBase.java Mon Dec 10 00:20:47 2007 @@ -0,0 +1,153 @@ +package org.apache.ddlutils.model; + +/* + * 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 org.apache.ddlutils.util.StringUtils; + +/** + * Base class for indexes. + * + * @version $Revision: $ + */ +public abstract class IndexImplBase implements Index +{ + /** The name of the index. */ + protected String _name; + /** The columns making up the index. */ + protected ArrayList _columns = new ArrayList(); + + /** + * {@inheritDoc} + */ + public String getName() + { + return _name; + } + + /** + * {@inheritDoc} + */ + public void setName(String name) + { + _name = name; + } + + /** + * {@inheritDoc} + */ + public int getColumnCount() + { + return _columns.size(); + } + + /** + * {@inheritDoc} + */ + public IndexColumn getColumn(int idx) + { + return (IndexColumn)_columns.get(idx); + } + + /** + * {@inheritDoc} + */ + public IndexColumn[] getColumns() + { + return (IndexColumn[])_columns.toArray(new IndexColumn[_columns.size()]); + } + + /** + * {@inheritDoc} + */ + public boolean hasColumn(Column column) + { + for (int idx = 0; idx < _columns.size(); idx++) + { + IndexColumn curColumn = getColumn(idx); + + if (column.equals(curColumn.getColumn())) + { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public boolean hasColumn(String columnName, boolean caseSensitive) + { + for (int idx = 0; idx < _columns.size(); idx++) + { + IndexColumn curColumn = getColumn(idx); + + if (StringUtils.equals(columnName, curColumn.getName(), caseSensitive)) + { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + */ + public void addColumn(IndexColumn column) + { + if (column != null) + { + for (int idx = 0; idx < _columns.size(); idx++) + { + IndexColumn curColumn = getColumn(idx); + + if (curColumn.getOrdinalPosition() > column.getOrdinalPosition()) + { + _columns.add(idx, column); + return; + } + } + _columns.add(column); + } + } + + /** + * {@inheritDoc} + */ + public void removeColumn(IndexColumn column) + { + _columns.remove(column); + } + + /** + * {@inheritDoc} + */ + public void removeColumn(int idx) + { + _columns.remove(idx); + } + + /** + * {@inheritDoc} + */ + public abstract Object clone() throws CloneNotSupportedException; +} Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/NonUniqueIndex.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/NonUniqueIndex.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/NonUniqueIndex.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/NonUniqueIndex.java Mon Dec 10 00:20:47 2007 @@ -29,7 +29,7 @@ * * @version $Revision: 289996 $ */ -public class NonUniqueIndex extends IndexImpBase +public class NonUniqueIndex extends IndexImplBase { /** Unique ID for serialization purposes. */ private static final long serialVersionUID = -3591499395114850301L; @@ -46,6 +46,14 @@ * {@inheritDoc} */ public Object clone() throws CloneNotSupportedException + { + return getClone(); + } + + /** + * {@inheritDoc} + */ + public Index getClone() throws ModelException { NonUniqueIndex result = new NonUniqueIndex(); Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Reference.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Reference.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Reference.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Reference.java Mon Dec 10 00:20:47 2007 @@ -29,7 +29,7 @@ * * @version $Revision$ */ -public class Reference implements Cloneable, Serializable +public class Reference implements Serializable { /** Unique ID for serialization purposes. */ private static final long serialVersionUID = 6062467640266171664L; @@ -175,19 +175,6 @@ _foreignColumn = null; } _foreignColumnName = foreignColumnName; - } - - /** - * {@inheritDoc} - */ - public Object clone() throws CloneNotSupportedException - { - Reference result = (Reference)super.clone(); - - result._localColumnName = _localColumnName; - result._foreignColumnName = _foreignColumnName; - - return result; } /** Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/Table.java Mon Dec 10 00:20:47 2007 @@ -38,7 +38,7 @@ * * @version $Revision$ */ -public class Table implements Serializable, Cloneable +public class Table implements Serializable { /** Unique ID for serialization purposes. */ private static final long serialVersionUID = -5541154961302342608L; @@ -267,6 +267,16 @@ } /** + * Removes all columns of this table. Note that this does not change + * indexes or foreign keys, so it might leave the table object in + * an illegal state. + */ + public void removeAllColumns() + { + _columns.clear(); + } + + /** * Removes the indicated column. * * @param idx The index of the column to remove @@ -348,6 +358,14 @@ } /** + * Removes all foreign keys. + */ + public void removeAllForeignKeys() + { + _foreignKeys.clear(); + } + + /** * Removes the given foreign key. * * @param foreignKey The foreign key to remove @@ -708,6 +726,24 @@ } /** + * Returns the names of the primary key columns of this table. + * + * @return The primary key column names + */ + public String[] getPrimaryKeyColumnNames() + { + Column[] pkColumns = getPrimaryKeyColumns(); + String[] names = new String[pkColumns.length]; + + for (int colIdx = 0; colIdx < pkColumns.length; colIdx++) + { + names[colIdx] = pkColumns[colIdx].getName(); + } + + return names; + } + + /** * Returns the auto increment columns in this table. If no incrementcolumns * are found, it will return an empty array. * @@ -752,24 +788,6 @@ } } - /** - * {@inheritDoc} - */ - public Object clone() throws CloneNotSupportedException - { - Table result = (Table)super.clone(); - - result._catalog = _catalog; - result._schema = _schema; - result._name = _name; - result._type = _type; - result._columns = (ArrayList)_columns.clone(); - result._foreignKeys = (ArrayList)_foreignKeys.clone(); - result._indices = (ArrayList)_indices.clone(); - - return result; - } - /** * {@inheritDoc} */ Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/model/UniqueIndex.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/model/UniqueIndex.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/model/UniqueIndex.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/model/UniqueIndex.java Mon Dec 10 00:20:47 2007 @@ -30,7 +30,7 @@ * * @version $Revision$ */ -public class UniqueIndex extends IndexImpBase +public class UniqueIndex extends IndexImplBase { /** Unique ID for serialization purposes. */ private static final long serialVersionUID = -4097003126550294993L; @@ -47,6 +47,14 @@ * {@inheritDoc} */ public Object clone() throws CloneNotSupportedException + { + return getClone(); + } + + /** + * {@inheritDoc} + */ + public Index getClone() throws ModelException { UniqueIndex result = new UniqueIndex(); Added: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java?rev=602807&view=auto ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java (added) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java Mon Dec 10 00:20:47 2007 @@ -0,0 +1,85 @@ +package org.apache.ddlutils.platform; + +/* + * 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.Iterator; +import java.util.List; + +import org.apache.ddlutils.alteration.AddColumnChange; +import org.apache.ddlutils.alteration.AddPrimaryKeyChange; +import org.apache.ddlutils.alteration.ModelComparator; +import org.apache.ddlutils.alteration.TableChange; +import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate; +import org.apache.ddlutils.model.Table; + +/** + * This is the default predicate for filtering supported table definition changes + * in the {@link ModelComparator}. It is also useful as the base class for platform + * specific implementations. + * + * @version $Revision: $ + */ +public class DefaultTableDefinitionChangesPredicate implements TableDefinitionChangesPredicate +{ + /** + * {@inheritDoc} + */ + public boolean areSupported(Table intermediateTable, List changes) + { + for (Iterator changeIt = changes.iterator(); changeIt.hasNext();) + { + TableChange change = (TableChange)changeIt.next(); + + if (!isSupported(intermediateTable, change)) + { + return false; + } + } + return true; + } + + /** + * Checks whether the given change is suppored. + * + * @param intermediateTable The current table to which this change would be applied + * @param change The table change + * @return true if the change is supported + */ + protected boolean isSupported(Table intermediateTable, TableChange change) + { + if (change instanceof AddColumnChange) + { + AddColumnChange addColumnChange = (AddColumnChange)change; + + return addColumnChange.isAtEnd() && + (!addColumnChange.getNewColumn().isRequired() || + (addColumnChange.getNewColumn().getDefaultValue() != null) || + addColumnChange.getNewColumn().isAutoIncrement()); + } + else if (change instanceof AddPrimaryKeyChange) + { + return true; + } + else + { + return false; + } + } +} \ No newline at end of file Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/JdbcModelReader.java Mon Dec 10 00:20:47 2007 @@ -663,7 +663,7 @@ { Index index = table.getIndex(indexIdx); - if ((mustBeUnique == index.isUnique()) && matches(index, columnNames) && + if ((!mustBeUnique || index.isUnique()) && matches(index, columnNames) && isInternalForeignKeyIndex(metaData, table, fk, index)) { fk.setAutoIndexPresent(true); @@ -1065,10 +1065,11 @@ */ protected void determineAutoIncrementFromResultSetMetaData(Table table, Column[] columnsToCheck) throws SQLException { - if (columnsToCheck == null || columnsToCheck.length == 0) + if ((columnsToCheck == null) || (columnsToCheck.length == 0)) { return; } + StringBuffer query = new StringBuffer(); query.append("SELECT "); Modified: db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java URL: http://svn.apache.org/viewvc/db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java?rev=602807&r1=602806&r2=602807&view=diff ============================================================================== --- db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java (original) +++ db/ddlutils/trunk/src/java/org/apache/ddlutils/platform/PlatformImplBase.java Mon Dec 10 00:20:47 2007 @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.math.BigDecimal; import java.sql.Blob; import java.sql.Clob; @@ -35,6 +36,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -50,10 +53,34 @@ import org.apache.ddlutils.DdlUtilsException; import org.apache.ddlutils.Platform; import org.apache.ddlutils.PlatformInfo; +import org.apache.ddlutils.alteration.AddColumnChange; +import org.apache.ddlutils.alteration.AddForeignKeyChange; +import org.apache.ddlutils.alteration.AddIndexChange; +import org.apache.ddlutils.alteration.AddPrimaryKeyChange; +import org.apache.ddlutils.alteration.AddTableChange; +import org.apache.ddlutils.alteration.ColumnDefinitionChange; +import org.apache.ddlutils.alteration.ColumnOrderChange; +import org.apache.ddlutils.alteration.ForeignKeyChange; +import org.apache.ddlutils.alteration.IndexChange; +import org.apache.ddlutils.alteration.ModelChange; +import org.apache.ddlutils.alteration.ModelComparator; +import org.apache.ddlutils.alteration.PrimaryKeyChange; +import org.apache.ddlutils.alteration.RecreateTableChange; +import org.apache.ddlutils.alteration.RemoveColumnChange; +import org.apache.ddlutils.alteration.RemoveForeignKeyChange; +import org.apache.ddlutils.alteration.RemoveIndexChange; +import org.apache.ddlutils.alteration.RemovePrimaryKeyChange; +import org.apache.ddlutils.alteration.RemoveTableChange; +import org.apache.ddlutils.alteration.TableChange; +import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate; import org.apache.ddlutils.dynabean.SqlDynaClass; import org.apache.ddlutils.dynabean.SqlDynaProperty; +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.ModelException; import org.apache.ddlutils.model.Table; import org.apache.ddlutils.model.TypeMap; import org.apache.ddlutils.util.Jdbc3Utils; @@ -402,16 +429,15 @@ */ public void createTables(Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException { - Connection connection = borrowConnection(); + createModel(model, dropTablesFirst, continueOnError); + } - try - { - createTables(connection, model, dropTablesFirst, continueOnError); - } - finally - { - returnConnection(connection); - } + /** + * {@inheritDoc} + */ + public void createTables(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + { + createModel(model, params, dropTablesFirst, continueOnError); } /** @@ -419,9 +445,15 @@ */ public void createTables(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException { - String sql = getCreateTablesSql(model, dropTablesFirst, continueOnError); + createModel(connection, model, dropTablesFirst, continueOnError); + } - evaluateBatch(connection, sql, continueOnError); + /** + * {@inheritDoc} + */ + public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + { + createModel(connection, model, params, dropTablesFirst, continueOnError); } /** @@ -429,33 +461,54 @@ */ public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError) { - String sql = null; + return getCreateTablesSql(model, dropTablesFirst, continueOnError); + } + + /** + * {@inheritDoc} + */ + public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) + { + return getCreateTablesSql(model, params, dropTablesFirst, continueOnError); + } + + /** + * {@inheritDoc} + */ + public void createModel(Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + { + Connection connection = borrowConnection(); try { - StringWriter buffer = new StringWriter(); - - getSqlBuilder().setWriter(buffer); - getSqlBuilder().createTables(model, dropTablesFirst); - sql = buffer.toString(); + createModel(connection, model, dropTablesFirst, continueOnError); } - catch (IOException e) + finally { - // won't happen because we're using a string writer + returnConnection(connection); } - return sql; } /** * {@inheritDoc} */ - public void createTables(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + public void createModel(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + { + String sql = getCreateModelSql(model, dropTablesFirst, continueOnError); + + evaluateBatch(connection, sql, continueOnError); + } + + /** + * {@inheritDoc} + */ + public void createModel(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException { Connection connection = borrowConnection(); try { - createTables(connection, model, params, dropTablesFirst, continueOnError); + createModel(connection, model, params, dropTablesFirst, continueOnError); } finally { @@ -466,9 +519,9 @@ /** * {@inheritDoc} */ - public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException + public void createModel(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException { - String sql = getCreateTablesSql(model, params, dropTablesFirst, continueOnError); + String sql = getCreateModelSql(model, params, dropTablesFirst, continueOnError); evaluateBatch(connection, sql, continueOnError); } @@ -476,7 +529,29 @@ /** * {@inheritDoc} */ - public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) + public String getCreateModelSql(Database model, boolean dropTablesFirst, boolean continueOnError) + { + String sql = null; + + try + { + StringWriter buffer = new StringWriter(); + + getSqlBuilder().setWriter(buffer); + getSqlBuilder().createTables(model, dropTablesFirst); + sql = buffer.toString(); + } + catch (IOException e) + { + // won't happen because we're using a string writer + } + return sql; + } + + /** + * {@inheritDoc} + */ + public String getCreateModelSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) { String sql = null; @@ -496,15 +571,100 @@ } /** + * Returns the model comparator to be used for this platform. This method is intendeded + * to be redefined by platforms that need to customize the model reader. + * + * @return The model comparator + */ + protected ModelComparator getModelComparator() + { + return new ModelComparator(getPlatformInfo(), + getTableDefinitionChangesPredicate(), + isDelimitedIdentifierModeOn()); + } + + /** + * Returns the predicate that defines which changes are supported by the platform. + * + * @return The predicate + */ + protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate() + { + return new DefaultTableDefinitionChangesPredicate(); + } + + /** * {@inheritDoc} */ - public void alterTables(Database desiredDb, boolean continueOnError) throws DatabaseOperationException + public List getChanges(Database currentModel, Database desiredModel) + { + List changes = getModelComparator().compare(currentModel, desiredModel); + + return sortChanges(changes); + } + + /** + * Sorts the changes so that they can be executed by the database. E.g. tables need to be created before + * they can be referenced by foreign keys, indexes should be dropped before a table is dropped etc. + * + * @param changes The original changes + * @return The sorted changes - this can be the original list object or a new one + */ + protected List sortChanges(List changes) + { + final Map typeOrder = new HashMap(); + + typeOrder.put(RemoveForeignKeyChange.class, new Integer(0)); + typeOrder.put(RemoveIndexChange.class, new Integer(1)); + typeOrder.put(RemoveTableChange.class, new Integer(2)); + typeOrder.put(RecreateTableChange.class, new Integer(3)); + typeOrder.put(RemovePrimaryKeyChange.class, new Integer(3)); + typeOrder.put(RemoveColumnChange.class, new Integer(4)); + typeOrder.put(ColumnDefinitionChange.class, new Integer(5)); + typeOrder.put(ColumnOrderChange.class, new Integer(5)); + typeOrder.put(AddColumnChange.class, new Integer(5)); + typeOrder.put(PrimaryKeyChange.class, new Integer(5)); + typeOrder.put(AddPrimaryKeyChange.class, new Integer(6)); + typeOrder.put(AddTableChange.class, new Integer(7)); + typeOrder.put(AddIndexChange.class, new Integer(8)); + typeOrder.put(AddForeignKeyChange.class, new Integer(9)); + + Collections.sort(changes, new Comparator() + { + public int compare(Object objA, Object objB) + { + Integer orderValueA = (Integer)typeOrder.get(objA.getClass()); + Integer orderValueB = (Integer)typeOrder.get(objB.getClass()); + + if (orderValueA == null) + { + return (orderValueB == null ? 0 : 1); + } + else if (orderValueB == null) + { + return -1; + } + else + { + return orderValueA.compareTo(orderValueB); + } + } + }); + return changes; + } + + /** + * {@inheritDoc} + */ + public void alterTables(Database desiredModel, boolean continueOnError) throws DatabaseOperationException { Connection connection = borrowConnection(); try { - alterTables(connection, desiredDb, continueOnError); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); + + alterModel(currentModel, desiredModel, continueOnError); } finally { @@ -515,13 +675,15 @@ /** * {@inheritDoc} */ - public String getAlterTablesSql(Database desiredDb) throws DatabaseOperationException + public void alterTables(Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException { Connection connection = borrowConnection(); try { - return getAlterTablesSql(connection, desiredDb); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); + + alterModel(currentModel, desiredModel, params, continueOnError); } finally { @@ -532,13 +694,15 @@ /** * {@inheritDoc} */ - public void alterTables(Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException + public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel, boolean continueOnError) throws DatabaseOperationException { Connection connection = borrowConnection(); try { - alterTables(connection, desiredDb, params, continueOnError); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + + alterModel(currentModel, desiredModel, continueOnError); } finally { @@ -549,13 +713,15 @@ /** * {@inheritDoc} */ - public String getAlterTablesSql(Database desiredDb, CreationParameters params) throws DatabaseOperationException + public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException { Connection connection = borrowConnection(); try { - return getAlterTablesSql(connection, desiredDb, params); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + + alterModel(currentModel, desiredModel, params, continueOnError); } finally { @@ -568,119 +734,99 @@ */ public void alterTables(Connection connection, Database desiredModel, boolean continueOnError) throws DatabaseOperationException { - String sql = getAlterTablesSql(connection, desiredModel); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); - evaluateBatch(connection, sql, continueOnError); + alterModel(currentModel, desiredModel, continueOnError); } /** * {@inheritDoc} */ - public String getAlterTablesSql(Connection connection, Database desiredModel) throws DatabaseOperationException + public void alterTables(Connection connection, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException { - String sql = null; Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); - try - { - StringWriter buffer = new StringWriter(); - - getSqlBuilder().setWriter(buffer); - getSqlBuilder().alterDatabase(currentModel, desiredModel, null); - sql = buffer.toString(); - } - catch (IOException ex) - { - // won't happen because we're using a string writer - } - return sql; + alterModel(currentModel, desiredModel, params, continueOnError); } /** * {@inheritDoc} */ - public void alterTables(Connection connection, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException + public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, boolean continueOnError) throws DatabaseOperationException { - String sql = getAlterTablesSql(connection, desiredModel, params); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); - evaluateBatch(connection, sql, continueOnError); + alterModel(currentModel, desiredModel, continueOnError); } /** * {@inheritDoc} */ - public String getAlterTablesSql(Connection connection, Database desiredModel, CreationParameters params) throws DatabaseOperationException + public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException { - String sql = null; - Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); - - try - { - StringWriter buffer = new StringWriter(); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); - getSqlBuilder().setWriter(buffer); - getSqlBuilder().alterDatabase(currentModel, desiredModel, params); - sql = buffer.toString(); - } - catch (IOException ex) - { - // won't happen because we're using a string writer - } - return sql; + alterModel(currentModel, desiredModel, params, continueOnError); } - /** + /** * {@inheritDoc} */ - public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel, boolean continueOnError) throws DatabaseOperationException - { + public String getAlterTablesSql(Database desiredModel) throws DatabaseOperationException + { Connection connection = borrowConnection(); try { - alterTables(connection, catalog, schema, tableTypes, desiredModel, continueOnError); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); + + return getAlterModelSql(currentModel, desiredModel); } finally { returnConnection(connection); } - } + } - /** + /** * {@inheritDoc} */ - public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredModel) throws DatabaseOperationException - { + public String getAlterTablesSql(Database desiredModel, CreationParameters params) throws DatabaseOperationException + { Connection connection = borrowConnection(); try { - return getAlterTablesSql(connection, catalog, schema, tableTypes, desiredModel); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); + + return getAlterModelSql(currentModel, desiredModel, params); } finally { returnConnection(connection); } - } + } - /** + /** * {@inheritDoc} */ - public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException - { + public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredModel) throws DatabaseOperationException + { Connection connection = borrowConnection(); try { - alterTables(connection, catalog, schema, tableTypes, desiredModel, params, continueOnError); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + + return getAlterModelSql(currentModel, desiredModel); } finally { returnConnection(connection); } - } + } - /** + /** * {@inheritDoc} */ public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params) throws DatabaseOperationException @@ -689,38 +835,78 @@ try { - return getAlterTablesSql(connection, catalog, schema, tableTypes, desiredModel, params); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + + return getAlterModelSql(currentModel, desiredModel, params); } finally { returnConnection(connection); } - } + } - /** + /** * {@inheritDoc} */ - public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, boolean continueOnError) throws DatabaseOperationException + public String getAlterTablesSql(Connection connection, Database desiredModel) throws DatabaseOperationException { - String sql = getAlterTablesSql(connection, catalog, schema, tableTypes, desiredModel); + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); - evaluateBatch(connection, sql, continueOnError); - } + return getAlterModelSql(currentModel, desiredModel); + } - /** + /** + * {@inheritDoc} + */ + public String getAlterTablesSql(Connection connection, Database desiredModel, CreationParameters params) throws DatabaseOperationException + { + Database currentModel = readModelFromDatabase(connection, desiredModel.getName()); + + return getAlterModelSql(currentModel, desiredModel, params); + } + + /** * {@inheritDoc} */ - public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel) throws DatabaseOperationException - { - String sql = null; + public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel) throws DatabaseOperationException + { Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + return getAlterModelSql(currentModel, desiredModel); + } + + /** + * {@inheritDoc} + */ + public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params) throws DatabaseOperationException + { + Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + + return getAlterModelSql(currentModel, desiredModel, params); + } + + /** + * {@inheritDoc} + */ + public String getAlterModelSql(Database currentModel, Database desiredModel) throws DatabaseOperationException + { + return getAlterModelSql(currentModel, desiredModel, null); + } + + /** + * {@inheritDoc} + */ + public String getAlterModelSql(Database currentModel, Database desiredModel, CreationParameters params) throws DatabaseOperationException + { + List changes = getChanges(currentModel, desiredModel); + String sql = null; + try { StringWriter buffer = new StringWriter(); getSqlBuilder().setWriter(buffer); - getSqlBuilder().alterDatabase(currentModel, desiredModel, null); + processChanges(currentModel, changes, params); sql = buffer.toString(); } catch (IOException ex) @@ -728,40 +914,61 @@ // won't happen because we're using a string writer } return sql; - } + } - /** + /** * {@inheritDoc} */ - public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException - { - String sql = getAlterTablesSql(connection, catalog, schema, tableTypes, desiredModel, params); + public void alterModel(Database currentModel, Database desiredModel, boolean continueOnError) throws DatabaseOperationException + { + Connection connection = borrowConnection(); - evaluateBatch(connection, sql, continueOnError); - } + try + { + alterModel(connection, currentModel, desiredModel, continueOnError); + } + finally + { + returnConnection(connection); + } + } - /** + /** * {@inheritDoc} */ - public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params) throws DatabaseOperationException - { - String sql = null; - Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes); + public void alterModel(Database currentModel, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException + { + Connection connection = borrowConnection(); try { - StringWriter buffer = new StringWriter(); - - getSqlBuilder().setWriter(buffer); - getSqlBuilder().alterDatabase(currentModel, desiredModel, params); - sql = buffer.toString(); + alterModel(connection, currentModel, desiredModel, params, continueOnError); } - catch (IOException ex) + finally { - // won't happen because we're using a string writer + returnConnection(connection); } - return sql; - } + } + + /** + * {@inheritDoc} + */ + public void alterModel(Connection connection, Database currentModel, Database desiredModel, boolean continueOnError) throws DatabaseOperationException + { + String sql = getAlterModelSql(currentModel, desiredModel); + + evaluateBatch(connection, sql, continueOnError); + } + + /** + * {@inheritDoc} + */ + public void alterModel(Connection connection, Database currentModel, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException + { + String sql = getAlterModelSql(currentModel, desiredModel, params); + + evaluateBatch(connection, sql, continueOnError); + } /** * {@inheritDoc} @@ -817,11 +1024,35 @@ */ public void dropTables(Database model, boolean continueOnError) throws DatabaseOperationException { + dropModel(model, continueOnError); + } + + /** + * {@inheritDoc} + */ + public void dropTables(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException + { + dropModel(connection, model, continueOnError); + } + + /** + * {@inheritDoc} + */ + public String getDropTablesSql(Database model, boolean continueOnError) + { + return getDropModelSql(model); + } + + /** + * {@inheritDoc} + */ + public void dropModel(Database model, boolean continueOnError) throws DatabaseOperationException + { Connection connection = borrowConnection(); try { - dropTables(connection, model, continueOnError); + dropModel(connection, model, continueOnError); } finally { @@ -832,9 +1063,9 @@ /** * {@inheritDoc} */ - public void dropTables(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException + public void dropModel(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException { - String sql = getDropTablesSql(model, continueOnError); + String sql = getDropModelSql(model); evaluateBatch(connection, sql, continueOnError); } @@ -842,7 +1073,7 @@ /** * {@inheritDoc} */ - public String getDropTablesSql(Database model, boolean continueOnError) + public String getDropModelSql(Database model) { String sql = null; @@ -862,6 +1093,405 @@ } /** + * Processes the given changes in the specified order. Basically, this method finds the + * appropriate handler method (one of the processChange methods) defined in + * the concrete sql builder for each change, and invokes it. + * + * @param model The database model; this object is not going to be changed by this method + * @param changes The changes + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @return The changed database model + */ + protected Database processChanges(Database model, + Collection changes, + CreationParameters params) throws IOException, DdlUtilsException + { + Database currentModel = new CloneHelper().clone(model); + + for (Iterator it = changes.iterator(); it.hasNext();) + { + invokeChangeHandler(currentModel, params, (ModelChange)it.next()); + } + return currentModel; + } + + /** + * Invokes the change handler (one of the processChange methods) for the given + * change object. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + private void invokeChangeHandler(Database currentModel, + CreationParameters params, + ModelChange change) throws IOException + { + Class curClass = getClass(); + + // find the handler for the change + while ((curClass != null) && !Object.class.equals(curClass)) + { + try + { + Method method = null; + + try + { + method = curClass.getDeclaredMethod("processChange", + new Class[] { Database.class, + CreationParameters.class, + change.getClass() }); + } + catch (NoSuchMethodException ex) + { + // we actually expect this one + } + + if (method != null) + { + method.invoke(this, new Object[] { currentModel, params, change }); + return; + } + else + { + curClass = curClass.getSuperclass(); + } + } + catch (InvocationTargetException ex) + { + if (ex.getTargetException() instanceof IOException) + { + throw (IOException)ex.getTargetException(); + } + else + { + throw new DdlUtilsException(ex.getTargetException()); + } + } + catch (Exception ex) + { + throw new DdlUtilsException(ex); + } + } + throw new DdlUtilsException("No handler for change of type " + change.getClass().getName() + " defined"); + } + + /** + * Finds the table changed by the change object in the given model. + * + * @param currentModel The model to find the table in + * @param change The table change + * @return The table + * @throws ModelException If the table could not be found + */ + protected Table findChangedTable(Database currentModel, TableChange change) throws ModelException + { + Table table = currentModel.findTable(change.getChangedTable(), + getPlatformInfo().isDelimitedIdentifiersSupported()); + + if (table == null) + { + throw new ModelException("Could not find table " + change.getChangedTable() + " in the given model"); + } + else + { + return table; + } + } + + /** + * Finds the index changed by the change object in the given model. + * + * @param currentModel The model to find the index in + * @param change The index change + * @return The index + * @throws ModelException If the index could not be found + */ + protected Index findChangedIndex(Database currentModel, IndexChange change) throws ModelException + { + Index index = change.findChangedIndex(currentModel, + getPlatformInfo().isDelimitedIdentifiersSupported()); + + if (index == null) + { + throw new ModelException("Could not find the index to change in table " + change.getChangedTable() + " in the given model"); + } + else + { + return index; + } + } + + /** + * Finds the foreign key changed by the change object in the given model. + * + * @param currentModel The model to find the foreign key in + * @param change The foreign key change + * @return The foreign key + * @throws ModelException If the foreign key could not be found + */ + protected ForeignKey findChangedForeignKey(Database currentModel, ForeignKeyChange change) throws ModelException + { + ForeignKey fk = change.findChangedForeignKey(currentModel, + getPlatformInfo().isDelimitedIdentifiersSupported()); + + if (fk == null) + { + throw new ModelException("Could not find the foreign key to change in table " + change.getChangedTable() + " in the given model"); + } + else + { + return fk; + } + } + + /** + * Processes a change representing the addition of a table. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + AddTableChange change) throws IOException + { + getSqlBuilder().createTable(currentModel, + change.getNewTable(), + params == null ? null : params.getParametersFor(change.getNewTable())); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the removal of a table. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + RemoveTableChange change) throws IOException, ModelException + { + Table changedTable = findChangedTable(currentModel, change); + + getSqlBuilder().dropTable(changedTable); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the addition of a foreign key. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + AddForeignKeyChange change) throws IOException + { + Table changedTable = findChangedTable(currentModel, change); + + getSqlBuilder().createForeignKey(currentModel, + changedTable, + change.getNewForeignKey()); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the removal of a foreign key. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + RemoveForeignKeyChange change) throws IOException, ModelException + { + Table changedTable = findChangedTable(currentModel, change); + ForeignKey changedFk = findChangedForeignKey(currentModel, change); + + getSqlBuilder().dropForeignKey(changedTable, changedFk); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the addition of an index. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + AddIndexChange change) throws IOException + { + Table changedTable = findChangedTable(currentModel, change); + + getSqlBuilder().createIndex(changedTable, change.getNewIndex()); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the removal of an index. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + RemoveIndexChange change) throws IOException, ModelException + { + Table changedTable = findChangedTable(currentModel, change); + Index changedIndex = findChangedIndex(currentModel, change); + + getSqlBuilder().dropIndex(changedTable, changedIndex); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the addition of a column. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + AddColumnChange change) throws IOException + { + Table changedTable = findChangedTable(currentModel, change); + + getSqlBuilder().addColumn(changedTable, change.getNewColumn()); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the addition of a primary key. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + AddPrimaryKeyChange change) throws IOException + { + Table changedTable = findChangedTable(currentModel, change); + String[] pkColumnNames = change.getPrimaryKeyColumns(); + Column[] pkColumns = new Column[pkColumnNames.length]; + + for (int colIdx = 0; colIdx < pkColumns.length; colIdx++) + { + pkColumns[colIdx] = changedTable.findColumn(pkColumnNames[colIdx], isDelimitedIdentifierModeOn()); + } + getSqlBuilder().createPrimaryKey(changedTable, pkColumns); + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Processes a change representing the recreation of a table. + * + * @param currentModel The current database schema + * @param params The parameters used in the creation of new tables. Note that for existing + * tables, the parameters won't be applied + * @param change The change object + */ + public void processChange(Database currentModel, + CreationParameters params, + RecreateTableChange change) throws IOException + { + // we can only copy the data if no required columns without default value and + // non-autoincrement have been added + boolean canMigrateData = true; + + for (Iterator it = change.getOriginalChanges().iterator(); canMigrateData && it.hasNext();) + { + TableChange curChange = (TableChange)it.next(); + + if (curChange instanceof AddColumnChange) + { + AddColumnChange addColumnChange = (AddColumnChange)curChange; + + if (addColumnChange.getNewColumn().isRequired() && + !addColumnChange.getNewColumn().isAutoIncrement() && + (addColumnChange.getNewColumn().getDefaultValue() == null)) + { + _log.warn("Data cannot be retained in table " + change.getChangedTable() + + " because of the addition of the required column " + addColumnChange.getNewColumn().getName()); + canMigrateData = false; + } + } + } + + Table changedTable = findChangedTable(currentModel, change); + Table targetTable = change.getTargetTable(); + Map parameters = (params == null ? null : params.getParametersFor(targetTable)); + + if (canMigrateData) + { + Table tempTable = getTemporaryTableFor(targetTable); + + getSqlBuilder().createTemporaryTable(currentModel, tempTable, parameters); + getSqlBuilder().copyData(changedTable, tempTable); + // Note that we don't drop the indices here because the DROP TABLE will take care of that + // Likewise, foreign keys have already been dropped as necessary + getSqlBuilder().dropTable(changedTable); + getSqlBuilder().createTable(currentModel, targetTable, parameters); + getSqlBuilder().copyData(tempTable, targetTable); + getSqlBuilder().dropTemporaryTable(currentModel, tempTable); + } + else + { + getSqlBuilder().dropTable(changedTable); + getSqlBuilder().createTable(currentModel, targetTable, params.getParametersFor(targetTable)); + } + + change.apply(currentModel, isDelimitedIdentifierModeOn()); + } + + /** + * Creates a temporary table object that corresponds to the given table. + * Database-specific implementations may redefine this method if e.g. the + * database directly supports temporary tables. The default implementation + * simply appends an underscore to the table name and uses that as the + * table name. + * + * @param targetTable The target table + * @return The temporary table + */ + protected Table getTemporaryTableFor(Table targetTable) + { + CloneHelper cloneHelper = new CloneHelper(); + Table table = new Table(); + + table.setCatalog(targetTable.getCatalog()); + table.setSchema(targetTable.getSchema()); + table.setName(targetTable.getName() + "_"); + table.setType(targetTable.getType()); + for (int idx = 0; idx < targetTable.getColumnCount(); idx++) + { + // TODO: clone PK status ? + table.addColumn(cloneHelper.clone(targetTable.getColumn(idx), true)); + } + + return table; + } + + /** * {@inheritDoc} */ public Iterator query(Database model, String sql) throws DatabaseOperationException @@ -1607,7 +2237,8 @@ * @param dynaClass The type * @param primaryKeys The primary keys * @param properties The properties to write - * @param bean Optionally the concrete bean to update + * @param oldBean Contains column values to identify the rows to update (i.e. for the WHERE clause) + * @param newBean Contains the new column values to write * @return The SQL required to update the instance */ protected String createUpdateSql(Database model, SqlDynaClass dynaClass, SqlDynaProperty[] primaryKeys, SqlDynaProperty[] properties, DynaBean oldBean, DynaBean newBean)