You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ddlutils-dev@db.apache.org by to...@apache.org on 2007/12/10 09:21:39 UTC

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...

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 <code>null</code>. */
     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 <code>true</code> 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 <code>true</code> 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 <code>true</code> 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 <code>true</code> 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 <code>true</code> 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 <code>true</code> 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 <code>processChange</code> 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 <code>processChange</code> 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)