Implemented new model comparison algorithm and database alteration workflow
Started to add tests to cover database alteration in more detail


git-svn-id: https://svn.apache.org/repos/asf/db/ddlutils/trunk@602807 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/.classpath b/.classpath
index c03a842..3756f86 100644
--- a/.classpath
+++ b/.classpath
@@ -1,26 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<!-- 
-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.
--->
 <classpath>
 	<classpathentry kind="src" path="src/java"/>
 	<classpathentry kind="src" output="target/test" path="src/test"/>
-	<classpathentry kind="var" path="JRE_LIB" sourcepath="JRE_SRC"/>
 	<classpathentry exported="true" kind="lib" path="lib/commons-logging-1.0.4.jar"/>
 	<classpathentry exported="true" kind="lib" path="lib/dom4j-1.4.jar"/>
 	<classpathentry exported="true" kind="lib" path="lib/commons-collections-3.1.jar"/>
@@ -34,5 +15,6 @@
 	<classpathentry kind="lib" path="lib/build-only/junit-3.8.2.jar"/>
 	<classpathentry kind="lib" path="lib/stax-api-1.0.1.jar"/>
 	<classpathentry kind="lib" path="lib/log4j-1.2.8.jar"/>
+	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry kind="output" path="target/classes"/>
 </classpath>
diff --git a/build.xml b/build.xml
index cf01554..07fd07f 100644
--- a/build.xml
+++ b/build.xml
@@ -179,6 +179,10 @@
              author="false"
              version="true"
              packagenames="org.*">
+      <tag name="ant.type" scope="types" description="Marks an ant type"/>
+      <tag name="ant.task" scope="types" description="Marks an ant task"/>
+      <tag name="ant.required" scope="methods" description="Marks a required task property"/>
+      <tag name="ant.not-required" scope="methods" description="Marks an optional task property"/>
       <link href="http://java.sun.com/j2se/1.4.2/docs/api/"/> 
       <link href="http://jakarta.apache.org/commons/lang/apidocs/"/>
       <link href="http://jakarta.apache.org/commons/beanutils/apidocs/"/>
diff --git a/src/java/org/apache/ddlutils/Platform.java b/src/java/org/apache/ddlutils/Platform.java
index d0e3e57..c0b70d2 100644
--- a/src/java/org/apache/ddlutils/Platform.java
+++ b/src/java/org/apache/ddlutils/Platform.java
@@ -292,6 +292,7 @@
      * @param model           The database model

      * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #createModel(Database, boolean, boolean)} instead.

      */

     public void createTables(Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

 

@@ -302,26 +303,18 @@
      * @param model           The database model

      * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #createModel(Connection, Database, boolean, boolean)} instead.

      */

     public void createTables(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

 

     /**

-     * Returns the SQL for creating the tables defined in the database model.

-     * 

-     * @param model           The database model

-     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

-     * @param continueOnError Whether to continue executing the sql commands when an error occurred

-     * @return The SQL statements

-     */

-    public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError);

-

-    /**

      * Creates the tables defined in the database model.

      * 

      * @param model           The database model

      * @param params          The parameters used in the creation

      * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #createModel(Database, CreationParameters, boolean, boolean)} instead.

      */

     public void createTables(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

 

@@ -333,6 +326,7 @@
      * @param params          The parameters used in the creation

      * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #createModel(Connection, Database, CreationParameters, boolean, boolean)} instead.

      */

     public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

 

@@ -340,62 +334,230 @@
      * Returns the SQL for creating the tables defined in the database model.

      * 

      * @param model           The database model

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @return The SQL statements

+     * @deprecated Use {@link #getCreateModelSql(Database, boolean, boolean)} instead.

+     */

+    public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError);

+

+    /**

+     * Returns the SQL for creating the tables defined in the database model.

+     * 

+     * @param model           The database model

+     * @param params          The parameters used in the creation

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @return The SQL statements

+     * @deprecated Use {@link #getCreateModelSql(Database, CreationParameters, boolean, boolean)} instead.

+     */

+    public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError);

+

+    /**

+     * Creates the tables defined in the database model.

+     * 

+     * @param model           The database model

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     */

+    public void createModel(Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Creates the tables defined in the database model.

+     * 

+     * @param connection      The connection to the database

+     * @param model           The database model

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     */

+    public void createModel(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Creates the tables defined in the database model.

+     * 

+     * @param model           The database model

+     * @param params          The parameters used in the creation

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     */

+    public void createModel(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Creates the tables defined in the database model.

+     * 

+     * @param connection      The connection to the database

+     * @param model           The database model

+     * @param params          The parameters used in the creation

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     */

+    public void createModel(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Returns the SQL for creating the tables defined in the database model.

+     * 

+     * @param model           The database model

+     * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @return The SQL statements

+     */

+    public String getCreateModelSql(Database model, boolean dropTablesFirst, boolean continueOnError);

+

+    /**

+     * Returns the SQL for creating the tables defined in the database model.

+     * 

+     * @param model           The database model

      * @param params          The parameters used in the creation

      * @param dropTablesFirst Whether to drop the tables prior to creating them (anew)

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

      * @return The SQL statements

      */

-    public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError);

+    public String getCreateModelSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError);

+

+    /**

+     * Returns the necessary changes to apply to the current database to make it the desired one.

+     * These changes are in the correct order and have been adjusted for the current platform.

+     * 

+     * @param currentModel The current model

+     * @param desiredModel The desired model

+     * @return The list of changes, adjusted to the platform and sorted for execution

+     */

+    public List getChanges(Database currentModel, Database desiredModel);

 

     /**

      * Alters the database schema so that it match the given model.

      *

      * @param desiredDb       The desired database schema

      * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Database, Database, boolean)} together with

+     *             {@link #readModelFromDatabase(String)} instead.

      */

     public void alterTables(Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

 

     /**

-     * Returns the SQL for altering the database schema so that it match the given model.

-     *

-     * @param desiredDb The desired database schema

-     * @return The SQL statements

-     */

-    public String getAlterTablesSql(Database desiredDb) throws DatabaseOperationException;

-

-    /**

      * Alters the database schema so that it match the given model.

      *

      * @param desiredDb       The desired database schema

      * @param params          The parameters used in the creation

      * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Database, Database, CreationParameters, boolean)} together with

+     *             {@link #readModelFromDatabase(String)} instead.

      */

     public void alterTables(Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

 

     /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param catalog         The catalog in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param schema          The schema in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param tableTypes      The table types to read from the existing database;

+     *                        use <code>null</code> or an empty array for the platform-specific default value

+     * @param desiredDb       The desired database schema

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Database, Database, boolean)} together with

+     *             {@link #readModelFromDatabase(String, String, String, String[])} instead.

+     */

+    public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param catalog         The catalog in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param schema          The schema in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param tableTypes      The table types to read from the existing database;

+     *                        use <code>null</code> or an empty array for the platform-specific default value

+     * @param desiredDb       The desired database schema

+     * @param params          The parameters used in the creation

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Database, Database, CreationParameters, boolean)} together with

+     *             {@link #readModelFromDatabase(String, String, String, String[])} instead.

+     */

+    public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param desiredDb       The desired database schema

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Connection, Database, Database, boolean)} together with

+     *             {@link #readModelFromDatabase(Connection, String)} instead.

+     */

+    public void alterTables(Connection connection, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param desiredDb       The desired database schema

+     * @param params          The parameters used in the creation

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Connection, Database, Database, CreationParameters, boolean)} together with

+     *             {@link #readModelFromDatabase(Connection, String)} instead.

+     */

+    public void alterTables(Connection connection, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param catalog         The catalog in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param schema          The schema in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param tableTypes      The table types to read from the existing database;

+     *                        use <code>null</code> or an empty array for the platform-specific default value

+     * @param desiredDb       The desired database schema

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Connection, Database, Database, boolean)} together with

+     *             {@link #readModelFromDatabase(Connection, String, String, String, String[])} instead.

+     */

+    public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the database schema so that it match the given model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param catalog         The catalog in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param schema          The schema in the existing database to read (can be a pattern);

+     *                        use <code>null</code> for the platform-specific default value

+     * @param tableTypes      The table types to read from the existing database;

+     *                        use <code>null</code> or an empty array for the platform-specific default value

+     * @param desiredDb       The desired database schema

+     * @param params          The parameters used in the creation

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     * @deprecated Use {@link #alterModel(Connection, Database, Database, CreationParameters, boolean)} together with

+     *             {@link #readModelFromDatabase(Connection, String, String, String, String[])} instead.

+     */

+    public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Returns the SQL for altering the database schema so that it match the given model.

+     *

+     * @param desiredDb The desired database schema

+     * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database)} together with

+     *             {@link #readModelFromDatabase(String)} instead.

+     */

+    public String getAlterTablesSql(Database desiredDb) throws DatabaseOperationException;

+

+    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param desiredDb The desired database schema

      * @param params    The parameters used in the creation

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database, CreationParameters)} together with

+     *             {@link #readModelFromDatabase(String)} instead.

      */

     public String getAlterTablesSql(Database desiredDb, CreationParameters params) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param catalog         The catalog in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param schema          The schema in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param tableTypes      The table types to read from the existing database;

-     *                        use <code>null</code> or an empty array for the platform-specific default value

-     * @param desiredDb       The desired database schema

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param catalog    The catalog in the existing database to read (can be a pattern);

@@ -406,25 +568,12 @@
      *                   use <code>null</code> or an empty array for the platform-specific default value

      * @param desiredDb  The desired database schema

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database)} together with

+     *             {@link #readModelFromDatabase(String, String, String, String[])} instead.

      */

     public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredDb) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param catalog         The catalog in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param schema          The schema in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param tableTypes      The table types to read from the existing database;

-     *                        use <code>null</code> or an empty array for the platform-specific default value

-     * @param desiredDb       The desired database schema

-     * @param params          The parameters used in the creation

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param catalog    The catalog in the existing database to read (can be a pattern);

@@ -436,63 +585,35 @@
      * @param desiredDb  The desired database schema

      * @param params     The parameters used in the creation

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database, CreationParameters)} together with

+     *             {@link #readModelFromDatabase(String, String, String, String[])} instead.

      */

     public String getAlterTablesSql(String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param connection      A connection to the existing database that shall be modified

-     * @param desiredDb       The desired database schema

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(Connection connection, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param connection A connection to the existing database that shall be modified

      * @param desiredDb  The desired database schema

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database)} together with

+     *             {@link #readModelFromDatabase(Connection, String)} instead.

      */

     public String getAlterTablesSql(Connection connection, Database desiredDb) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param connection      A connection to the existing database that shall be modified

-     * @param desiredDb       The desired database schema

-     * @param params          The parameters used in the creation

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(Connection connection, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param connection A connection to the existing database that shall be modified

      * @param desiredDb  The desired database schema

      * @param params     The parameters used in the creation

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database, CreationParameters)} together with

+     *             {@link #readModelFromDatabase(Connection, String)} instead.

      */

     public String getAlterTablesSql(Connection connection, Database desiredDb, CreationParameters params) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param connection      A connection to the existing database that shall be modified

-     * @param catalog         The catalog in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param schema          The schema in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param tableTypes      The table types to read from the existing database;

-     *                        use <code>null</code> or an empty array for the platform-specific default value

-     * @param desiredDb       The desired database schema

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param connection A connection to the existing database that shall be modified

@@ -504,26 +625,12 @@
      *                   use <code>null</code> or an empty array for the platform-specific default value

      * @param desiredDb  The desired database schema

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database)} together with

+     *             {@link #readModelFromDatabase(Connection, String, String, String, String[])} instead.

      */

     public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb) throws DatabaseOperationException;

 

     /**

-     * Alters the database schema so that it match the given model.

-     *

-     * @param connection      A connection to the existing database that shall be modified

-     * @param catalog         The catalog in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param schema          The schema in the existing database to read (can be a pattern);

-     *                        use <code>null</code> for the platform-specific default value

-     * @param tableTypes      The table types to read from the existing database;

-     *                        use <code>null</code> or an empty array for the platform-specific default value

-     * @param desiredDb       The desired database schema

-     * @param params          The parameters used in the creation

-     * @param continueOnError Whether to continue with the next sql statement when an error occurred

-     */

-    public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

      * Returns the SQL for altering the database schema so that it match the given model.

      *

      * @param connection A connection to the existing database that shall be modified

@@ -536,10 +643,71 @@
      * @param desiredDb  The desired database schema

      * @param params     The parameters used in the creation

      * @return The SQL statements

+     * @deprecated Use {@link #getAlterModelSql(Database, Database, CreationParameters)} together with

+     *             {@link #readModelFromDatabase(Connection, String, String, String, String[])} instead.

      */

     public String getAlterTablesSql(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredDb, CreationParameters params) throws DatabaseOperationException;

 

     /**

+     * Alters the given live database model so that it match the desired model, using the default database conneciton.

+     *

+     * @param currentModel    The current database model

+     * @param desiredModel    The desired database model

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     */

+    public void alterModel(Database currentModel, Database desiredModel, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the given live database model so that it match the desired model, using the default database conneciton.

+     *

+     * @param currentModel    The current database model

+     * @param desiredModel    The desired database model

+     * @param params          The parameters used in the creation

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     */

+    public void alterModel(Database currentModel, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the given live database model so that it match the desired model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param currentModel    The current database model

+     * @param desiredModel    The desired database model

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     */

+    public void alterModel(Connection connection, Database currentModel, Database desiredModel, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Alters the given live database model so that it match the desired model.

+     *

+     * @param connection      A connection to the existing database that shall be modified

+     * @param currentModel    The current database model

+     * @param desiredModel    The desired database model

+     * @param params          The parameters used in the creation

+     * @param continueOnError Whether to continue with the next sql statement when an error occurred

+     */

+    public void alterModel(Connection connection, Database currentModel, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Returns the SQL for altering the given current model so that it match the desired model.

+     *

+     * @param currentModel The current database model

+     * @param desiredModel The desired database model

+     * @return The SQL statements

+     */

+    public String getAlterModelSql(Database currentModel, Database desiredModel) throws DatabaseOperationException;

+

+    /**

+     * Returns the SQL for altering the given current model so that it match the desired model.

+     *

+     * @param currentModel The current database model

+     * @param desiredModel The desired database model

+     * @param params       The parameters used in the creation of tables etc.

+     * @return The SQL statements

+     */

+    public String getAlterModelSql(Database currentModel, Database desiredModel, CreationParameters params) throws DatabaseOperationException;

+

+    /**

      * Drops the specified table and all foreign keys pointing to it.

      * 

      * @param model           The database model

@@ -569,30 +737,58 @@
     public void dropTable(Connection connection, Database model, Table table, boolean continueOnError) throws DatabaseOperationException; 

 

     /**

-     * Drops the tables defined in the given database.

-     * 

-     * @param model           The database model

-     * @param continueOnError Whether to continue executing the sql commands when an error occurred

-     */

-    public void dropTables(Database model, boolean continueOnError) throws DatabaseOperationException;

-

-    /**

-     * Returns the SQL for dropping the tables defined in the given database.

+     * Returns the SQL for dropping the given model.

      * 

      * @param model           The database model

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

      * @return The SQL statements

+     * @deprecated Use {@link #getDropModelSql(Database)} instead.

      */

     public String getDropTablesSql(Database model, boolean continueOnError);

 

     /**

-     * Drops the tables defined in the given database.

+     * Drops the given model using the default database connection.

+     * 

+     * @param model           The database model

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #dropModel(Database, boolean)} instead.

+     */

+    public void dropTables(Database model, boolean continueOnError) throws DatabaseOperationException;

+

+    /**

+     * Drops the given model.

+     * 

+     * @param connection      The connection to the database

+     * @param model           The database model

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     * @deprecated Use {@link #dropModel(Connection, Database, boolean)} instead.

+     */

+    public void dropTables(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException; 

+

+    /**

+     * Returns the SQL for dropping the given model.

+     * 

+     * @param model The database model

+     * @return The SQL statements

+     */

+    public String getDropModelSql(Database model);

+

+    /**

+     * Drops the given model using the default database connection.

+     * 

+     * @param model           The database model

+     * @param continueOnError Whether to continue executing the sql commands when an error occurred

+     */

+    public void dropModel(Database model, boolean continueOnError) throws DatabaseOperationException; 

+

+    /**

+     * Drops the given model.

      * 

      * @param connection      The connection to the database

      * @param model           The database model

      * @param continueOnError Whether to continue executing the sql commands when an error occurred

      */

-    public void dropTables(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException; 

+    public void dropModel(Connection connection, Database model, boolean continueOnError) throws DatabaseOperationException; 

 

     /**

      * Performs the given SQL query returning an iterator over the results.

@@ -833,7 +1029,7 @@
      * @param newDynaBean The bean containing the new values

      * @return The update sql

      */

-    public String getUpdateSql(Database model, DynaBean oldDynaBean, DynaBean newDynaBan);

+    public String getUpdateSql(Database model, DynaBean oldDynaBean, DynaBean newDynaBean);

 

     /**

      * Updates the given bean in the database, assuming the primary key values are specified. Note that this means

diff --git a/src/java/org/apache/ddlutils/PlatformInfo.java b/src/java/org/apache/ddlutils/PlatformInfo.java
index 970144d..6b6d077 100644
--- a/src/java/org/apache/ddlutils/PlatformInfo.java
+++ b/src/java/org/apache/ddlutils/PlatformInfo.java
@@ -69,7 +69,13 @@
     private boolean _defaultValueUsedForIdentitySpec = false;

 

     // properties influencing the reading of models from live databases

-    

+

+    /** Whether primary key columns are automatically required. */

+    private boolean _primaryKeyColumnAutomaticallyRequired = false;

+

+    /** Whether identity columns are automatically required. */

+    private boolean _identityColumnAutomaticallyRequired = false;

+

     /** Whether system indices (database-generated indices for primary and foreign keys) are returned when

         reading a model from a database. */

     private boolean _systemIndicesReturned = true;

@@ -375,6 +381,48 @@
     // properties influencing the reading of models from live databases

 

     /**

+     * Determines whether the database will make a primary key column automatically required.

+     *  

+     * @return <code>true</code> if primary key columns are automatically required

+     */

+    public boolean isPrimaryKeyColumnAutomaticallyRequired()

+    {

+        return _primaryKeyColumnAutomaticallyRequired;

+    }

+

+    /**

+     * Specifies whether the database will make a primary key column automatically required.

+     * 

+     * @param primaryKeyAutomaticallyRequired <code>true</code> if primary key columns are

+     *                                        automatically required

+     */

+    public void setPrimaryKeyColumnAutomaticallyRequired(boolean primaryKeyAutomaticallyRequired)

+    {

+        _primaryKeyColumnAutomaticallyRequired = primaryKeyAutomaticallyRequired;

+    }

+

+    /**

+     * Determines whether the database will make an idenity column automatically required.

+     *  

+     * @return <code>true</code> if identity columns are automatically required

+     */

+    public boolean isIdentityColumnAutomaticallyRequired()

+    {

+        return _identityColumnAutomaticallyRequired;

+    }

+

+    /**

+     * Specifies whether the database will make a primary key column automatically required.

+     * 

+     * @param identityAutomaticallyRequired <code>true</code> if identity columns are

+     *                                      automatically required

+     */

+    public void setIdentityColumnAutomaticallyRequired(boolean identityAutomaticallyRequired)

+    {

+        _identityColumnAutomaticallyRequired = identityAutomaticallyRequired;

+    }

+

+    /**

      * Determines whether database-generated indices for primary and foreign keys are

      * returned when reading a model from a database.

      *

diff --git a/src/java/org/apache/ddlutils/alteration/AddColumnChange.java b/src/java/org/apache/ddlutils/alteration/AddColumnChange.java
index ea808d7..7ab72e8 100644
--- a/src/java/org/apache/ddlutils/alteration/AddColumnChange.java
+++ b/src/java/org/apache/ddlutils/alteration/AddColumnChange.java
@@ -19,7 +19,7 @@
  * under the License.

  */

 

-import org.apache.ddlutils.DdlUtilsException;

+import org.apache.ddlutils.model.CloneHelper;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Table;

@@ -33,27 +33,25 @@
 {

     /** The new column. */

     private Column _newColumn;

-    /** The column after which the new column should be added. */

-    private Column _previousColumn;

-    /** The column before which the new column should be added. */

-    private Column _nextColumn;

-    /** Whether the column is added at the end. */

-    private boolean _atEnd;

+    /** The name of the column after which the new column should be added. */

+    private String _previousColumnName;

+    /** The name of the column before which the new column should be added. */

+    private String _nextColumnName;

 

     /**

      * Creates a new change object.

      * 

-     * @param table          The table to add the column to

-     * @param newColumn      The new column

-     * @param previousColumn The column after which the new column should be added

-     * @param nextColumn     The column before which the new column should be added

+     * @param tableName          The name of the table to add the column to

+     * @param newColumn          The new column

+     * @param previousColumnName The name of the column after which the new column should be added

+     * @param nextColumnName     The name of the column before which the new column should be added

      */

-    public AddColumnChange(Table table, Column newColumn, Column previousColumn, Column nextColumn)

+    public AddColumnChange(String tableName, Column newColumn, String previousColumnName, String nextColumnName)

     {

-        super(table);

-        _newColumn      = newColumn;

-        _previousColumn = previousColumn;

-        _nextColumn     = nextColumn;

+        super(tableName);

+        _newColumn          = newColumn;

+        _previousColumnName = previousColumnName;

+        _nextColumnName     = nextColumnName;

     }

 

     /**

@@ -67,23 +65,23 @@
     }

 

     /**

-     * Returns the column after which the new column should be added.

+     * Returns the name of the column after which the new column should be added.

      *

-     * @return The previous column

+     * @return The name of the previous column

      */

-    public Column getPreviousColumn()

+    public String getPreviousColumn()

     {

-        return _previousColumn;

+        return _previousColumnName;

     }

 

     /**

-     * Returns the column before which the new column should be added.

+     * Returns the name of the column before which the new column should be added.

      *

-     * @return The next column

+     * @return The name of the next column

      */

-    public Column getNextColumn()

+    public String getNextColumn()

     {

-        return _nextColumn;

+        return _nextColumnName;

     }

 

     /**

@@ -94,18 +92,7 @@
      */

     public boolean isAtEnd()

     {

-        return _atEnd;

-    }

-

-    /**

-     * Specifies whether the column is added at the end (when applied in the order

-     * of creation of the changes).

-     * 

-     * @param atEnd <code>true</code> if the column is added at the end

-     */

-    public void setAtEnd(boolean atEnd)

-    {

-        _atEnd = atEnd;

+        return _nextColumnName == null;

     }

 

     /**

@@ -113,27 +100,20 @@
      */

     public void apply(Database model, boolean caseSensitive)

     {

-        Column newColumn = null;

+        Table  table     = findChangedTable(model, caseSensitive);

+        Column newColumn = new CloneHelper().clone(_newColumn, true);

 

-        try

+        if (_previousColumnName != null)

         {

-        	// TODO: Cloning should not be necessary

-            newColumn = (Column)_newColumn.clone();

-        }

-        catch (CloneNotSupportedException ex)

-        {

-            throw new DdlUtilsException(ex);

-        }

-

-        Table table = findChangedTable(model, caseSensitive);

-

-        // TODO: change this !

-        if ((_previousColumn != null) && (_nextColumn != null))

-        {

-            int idx = table.getColumnIndex(_previousColumn) + 1;

+            Column prevColumn = table.findColumn(_previousColumnName, caseSensitive);

+            int    idx        = table.getColumnIndex(prevColumn) + 1;

 

             table.addColumn(idx, newColumn);

         }

+        else if (_nextColumnName != null)

+        {

+            table.addColumn(0, newColumn);

+        }

         else

         {

             table.addColumn(newColumn);

diff --git a/src/java/org/apache/ddlutils/alteration/AddForeignKeyChange.java b/src/java/org/apache/ddlutils/alteration/AddForeignKeyChange.java
index 394c7c1..28b8c87 100644
--- a/src/java/org/apache/ddlutils/alteration/AddForeignKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/AddForeignKeyChange.java
@@ -19,10 +19,8 @@
  * under the License.

  */

 

-import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.ForeignKey;

-import org.apache.ddlutils.model.Table;

 

 /**

  * Represents the addition of a foreign key to a table. Note that for

@@ -39,12 +37,12 @@
     /**

      * Creates a new change object.

      * 

-     * @param table         The table to add the foreign key to

+     * @param tableName     The name of the table to add the foreign key to

      * @param newForeignKey The new foreign key

      */

-    public AddForeignKeyChange(Table table, ForeignKey newForeignKey)

+    public AddForeignKeyChange(String tableName, ForeignKey newForeignKey)

     {

-        super(table);

+        super(tableName);

         _newForeignKey = newForeignKey;

     }

 

@@ -63,19 +61,6 @@
      */

     public void apply(Database database, boolean caseSensitive)

     {

-        ForeignKey newFK = null;

-

-        try

-        {

-        	// TODO: we should not have to clone here

-            newFK = (ForeignKey)_newForeignKey.clone();

-            newFK.setForeignTable(database.findTable(_newForeignKey.getForeignTableName(), caseSensitive));

-        }

-        catch (CloneNotSupportedException ex)

-        {

-            throw new DdlUtilsException(ex);

-        }

-        findChangedTable(database, caseSensitive).addForeignKey(newFK);

+        findChangedTable(database, caseSensitive).addForeignKey(_newForeignKey);

     }

-

 }

diff --git a/src/java/org/apache/ddlutils/alteration/AddIndexChange.java b/src/java/org/apache/ddlutils/alteration/AddIndexChange.java
index 458d491..e727ad1 100644
--- a/src/java/org/apache/ddlutils/alteration/AddIndexChange.java
+++ b/src/java/org/apache/ddlutils/alteration/AddIndexChange.java
@@ -22,7 +22,6 @@
 import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Index;

-import org.apache.ddlutils.model.Table;

 

 /**

  * Represents the addition of an index to a table.

@@ -37,12 +36,12 @@
     /**

      * Creates a new change object.

      * 

-     * @param table    The table to add the index to

-     * @param newIndex The new index

+     * @param tableName The name of the table to add the index to

+     * @param newIndex  The new index

      */

-    public AddIndexChange(Table table, Index newIndex)

+    public AddIndexChange(String tableName, Index newIndex)

     {

-        super(table);

+        super(tableName);

         _newIndex = newIndex;

     }

 

diff --git a/src/java/org/apache/ddlutils/alteration/AddPrimaryKeyChange.java b/src/java/org/apache/ddlutils/alteration/AddPrimaryKeyChange.java
index cbed1d0..69aa832 100644
--- a/src/java/org/apache/ddlutils/alteration/AddPrimaryKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/AddPrimaryKeyChange.java
@@ -30,27 +30,27 @@
  */

 public class AddPrimaryKeyChange extends TableChangeImplBase

 {

-    /** The columns making up the primary key. */

-    private Column[] _primaryKeyColumns;

+    /** The names of the columns making up the primary key. */

+    private String[] _primaryKeyColumns;

 

     /**

      * Creates a new change object.

      * 

-     * @param table             The table to add the primary key to

-     * @param primaryKeyColumns The columns making up the primary key

+     * @param tableName         The name of the table to add the primary key to

+     * @param primaryKeyColumns The names of the columns making up the primary key

      */

-    public AddPrimaryKeyChange(Table table, Column[] primaryKeyColumns)

+    public AddPrimaryKeyChange(String tableName, String[] primaryKeyColumns)

     {

-        super(table);

+        super(tableName);

         _primaryKeyColumns = primaryKeyColumns;

     }

 

     /**

-     * Returns the primary key columns making up the new primary key.

+     * Returns the primary key column names making up the new primary key.

      *

-     * @return The primary key columns

+     * @return The primary key column names

      */

-    public Column[] getPrimaryKeyColumns()

+    public String[] getPrimaryKeyColumns()

     {

         return _primaryKeyColumns;

     }

@@ -64,7 +64,7 @@
 

         for (int idx = 0; idx < _primaryKeyColumns.length; idx++)

         {

-            Column column = table.findColumn(_primaryKeyColumns[idx].getName(), caseSensitive);

+            Column column = table.findColumn(_primaryKeyColumns[idx], caseSensitive);

 

             column.setPrimaryKey(true);

         }

diff --git a/src/java/org/apache/ddlutils/alteration/AddTableChange.java b/src/java/org/apache/ddlutils/alteration/AddTableChange.java
index 164b279..166f2dc 100644
--- a/src/java/org/apache/ddlutils/alteration/AddTableChange.java
+++ b/src/java/org/apache/ddlutils/alteration/AddTableChange.java
@@ -19,7 +19,7 @@
  * under the License.

  */

 

-import org.apache.ddlutils.DdlUtilsException;

+import org.apache.ddlutils.model.CloneHelper;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Table;

 

@@ -37,7 +37,8 @@
     /**

      * Creates a new change object.

      * 

-     * @param newTable The new table

+     * @param newTable The new table; note that the change object will keep a reference to this table

+     *                 which means that the table should not be changed after creating this change object

      */

     public AddTableChange(Table newTable)

     {

@@ -61,15 +62,8 @@
      */

     public void apply(Database database, boolean caseSensitive)

     {

-        try

-        {

-        	// TODO: we shouldn't have to clone here

-            database.addTable((Table)_newTable.clone());

-        }

-        catch (CloneNotSupportedException ex)

-        {

-            throw new DdlUtilsException(ex);

-        }

-    }

+        Table table = new CloneHelper().clone(_newTable, true, false, database, caseSensitive);

 

+        database.addTable(table);

+    }

 }

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnAutoIncrementChange.java b/src/java/org/apache/ddlutils/alteration/ColumnAutoIncrementChange.java
deleted file mode 100644
index a5e35d5..0000000
--- a/src/java/org/apache/ddlutils/alteration/ColumnAutoIncrementChange.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Represents the change of the auto-increment constraint of a column. Since it is a boolean value,

- * this means the required constraint will simply be toggled.

- * 

- * @version $Revision: $

- */

-public class ColumnAutoIncrementChange extends ColumnChangeImplBase

-{

-    /**

-     * Creates a new change object.

-     * 

-     * @param table  The table of the column

-     * @param column The column

-     */

-    public ColumnAutoIncrementChange(Table table, Column column)

-    {

-        super(table, column);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void apply(Database model, boolean caseSensitive)

-    {

-        findChangedColumn(model, caseSensitive).setAutoIncrement(!getChangedColumn().isAutoIncrement());

-    }

-}

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnChange.java b/src/java/org/apache/ddlutils/alteration/ColumnChange.java
index b6f46e6..22ab726 100644
--- a/src/java/org/apache/ddlutils/alteration/ColumnChange.java
+++ b/src/java/org/apache/ddlutils/alteration/ColumnChange.java
@@ -30,11 +30,11 @@
 public interface ColumnChange extends TableChange

 {

     /**

-     * Returns the affected column from the original model.

+     * Returns the name of the affected column from the original model.

      * 

-     * @return The affected column

+     * @return The name of the affected column

      */

-    public Column getChangedColumn();

+    public String getChangedColumn();

 

     /**

      * Finds the column object corresponding to the changed column in the given database model.

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnChangeImplBase.java b/src/java/org/apache/ddlutils/alteration/ColumnChangeImplBase.java
index 7156fe4..3972ed4 100644
--- a/src/java/org/apache/ddlutils/alteration/ColumnChangeImplBase.java
+++ b/src/java/org/apache/ddlutils/alteration/ColumnChangeImplBase.java
@@ -1,33 +1,57 @@
 package org.apache.ddlutils.alteration;
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Table;
 
+/**
+ * Base class for changes to columns.
+ * 
+ * @version $Revision: $
+ */
 public abstract class ColumnChangeImplBase extends    TableChangeImplBase
                                            implements ColumnChange
 {
-    /** The column. */
-    private Column _column;
+    /** The column's name. */
+    private String _columnName;
 
     /**
      * Creates a new change object.
      * 
-     * @param table  The table to remove the column from
-     * @param column The column
+     * @param tableName  The name of the table to remove the column from
+     * @param columnName The column's name
      */
-    public ColumnChangeImplBase(Table table, Column column)
+    public ColumnChangeImplBase(String tableName, String columnName)
     {
-        super(table);
-        _column = column;
+        super(tableName);
+        _columnName = columnName;
     }
 
     /**
      * {@inheritDoc}
      */
-    public Column getChangedColumn()
+    public String getChangedColumn()
     {
-        return _column;
+        return _columnName;
     }
 
     /**
@@ -37,6 +61,6 @@
     {
     	Table table = findChangedTable(model, caseSensitive);
 
-    	return table == null ? null : table.findColumn(_column.getName(), caseSensitive);
+    	return table == null ? null : table.findColumn(_columnName, caseSensitive);
     }
 }
diff --git a/src/java/org/apache/ddlutils/alteration/ColumnDataTypeChange.java b/src/java/org/apache/ddlutils/alteration/ColumnDataTypeChange.java
deleted file mode 100644
index 38d3e48..0000000
--- a/src/java/org/apache/ddlutils/alteration/ColumnDataTypeChange.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Represents the change of the data type of a column.

- * 

- * @version $Revision: $

- */

-public class ColumnDataTypeChange extends ColumnChangeImplBase

-{

-    /** The JDBC type code of the new type. */

-    private int _newTypeCode;

-

-    /**

-     * Creates a new change object.

-     * 

-     * @param table       The table of the column

-     * @param column      The column

-     * @param newTypeCode The JDBC type code of the new type

-     */

-    public ColumnDataTypeChange(Table table, Column column, int newTypeCode)

-    {

-        super(table, column);

-        _newTypeCode = newTypeCode;

-    }

-

-    /**

-     * Returns the JDBC type code of the new type.

-     *

-     * @return The type code

-     */

-    public int getNewTypeCode()

-    {

-        return _newTypeCode;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void apply(Database model, boolean caseSensitive)

-    {

-        findChangedColumn(model, caseSensitive).setTypeCode(_newTypeCode);

-    }

-}

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnDefaultValueChange.java b/src/java/org/apache/ddlutils/alteration/ColumnDefaultValueChange.java
deleted file mode 100644
index 54c96b7..0000000
--- a/src/java/org/apache/ddlutils/alteration/ColumnDefaultValueChange.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Represents the change of the default value of a column.

- * 

- * @version $Revision: $

- */

-public class ColumnDefaultValueChange extends ColumnChangeImplBase

-{

-    /** The new default value. */

-    private String _newDefaultValue;

-

-    /**

-     * Creates a new change object.

-     * 

-     * @param table           The table of the column

-     * @param column          The column

-     * @param newDefaultValue The new default value

-     */

-    public ColumnDefaultValueChange(Table table, Column column, String newDefaultValue)

-    {

-        super(table, column);

-        _newDefaultValue = newDefaultValue;

-    }

-

-    /**

-     * Returns the new default value.

-     *

-     * @return The new default value

-     */

-    public String getNewDefaultValue()

-    {

-        return _newDefaultValue;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void apply(Database model, boolean caseSensitive)

-    {

-        findChangedColumn(model, caseSensitive).setDefaultValue(_newDefaultValue);

-    }

-}

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnDefinitionChange.java b/src/java/org/apache/ddlutils/alteration/ColumnDefinitionChange.java
new file mode 100644
index 0000000..5201ad2
--- /dev/null
+++ b/src/java/org/apache/ddlutils/alteration/ColumnDefinitionChange.java
@@ -0,0 +1,182 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.util.StringUtils;

+

+/**

+ * Represents the change of one or more aspects of the definition of a column, such as

+ * data type or size, whether it is required or not, etc. Note that primary key status

+ * is not part of the definition.

+ * 

+ * @version $Revision: $

+ */

+public class ColumnDefinitionChange extends ColumnChangeImplBase

+{

+    /** The target column definition. */

+    private Column _newColumnDef;

+

+    /**

+     * Creates a new change object.

+     * 

+     * @param tableName    The name of the table owning the changed column

+     * @param columnName   The name of the changed column

+     * @param newColumnDef The new column definition

+     */

+    public ColumnDefinitionChange(String tableName, String columnName, Column newColumnDef)

+    {

+        super(tableName, columnName);

+        _newColumnDef = newColumnDef;

+    }

+

+    /**

+     * Returns the new column definition.

+     *

+     * @return The new column

+     */

+    public Column getNewColumn()

+    {

+        return _newColumnDef;

+    }

+

+    /**

+     * Determines whether the definition of the given target column is different from the one of the given source column.

+     * 

+     * @param platformInfo The info object for the current platform

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the definitions differ

+     */

+    public static boolean isChanged(PlatformInfo platformInfo, Column sourceColumn, Column targetColumn)

+    {

+        return isTypeChanged(platformInfo, sourceColumn, targetColumn) ||

+               isSizeChanged(platformInfo, sourceColumn, targetColumn) ||

+               isDefaultValueChanged(sourceColumn, targetColumn) ||

+               isRequiredStatusChanged(sourceColumn, targetColumn) ||

+               isAutoIncrementChanged(sourceColumn, targetColumn);

+    }

+

+    /**

+     * Determines whether the jdbc type of the given target column is different from the one of the given source column.

+     * This method uses the platform info object to determine the actual jdbc type that the target column would have

+     * in the database, and compares that to the type of he source column.

+     * 

+     * @param platformInfo The info object for the current platform

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the jdbc types differ

+     */

+    public static boolean isTypeChanged(PlatformInfo platformInfo, Column sourceColumn, Column targetColumn)

+    {

+        int targetTypeCode = platformInfo.getTargetJdbcType(targetColumn.getTypeCode());

+

+        return targetTypeCode != sourceColumn.getTypeCode();

+    }

+

+    /**

+     * Determines whether the size or precision/scale of the given target column is different from that of the given source column.

+     * If size and precision/scale do not matter for the target column's type, then <code>false</code> is returned.

+     * 

+     * @param platformInfo The info object for the current platform

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the sizes or precisions/scales differ

+     */

+    public static boolean isSizeChanged(PlatformInfo platformInfo, Column sourceColumn, Column targetColumn)

+    {

+        int     targetTypeCode = platformInfo.getTargetJdbcType(targetColumn.getTypeCode());

+        boolean sizeMatters    = platformInfo.hasSize(targetTypeCode);

+        boolean scaleMatters   = platformInfo.hasPrecisionAndScale(targetTypeCode);

+

+        if (sizeMatters && !StringUtils.equals(sourceColumn.getSize(), targetColumn.getSize()))

+        {

+            return true;

+        }

+        else if (scaleMatters &&

+                 (sourceColumn.getPrecisionRadix() != targetColumn.getPrecisionRadix()) ||

+                  (sourceColumn.getScale() != targetColumn.getScale()))

+        {

+            return true;

+        }

+        else

+        {

+            return false;

+        }

+    }

+

+    /**

+     * Determines whether the default value of the given target column is different from the one of the given source column.

+     * This method compares the parsed default values instead of their representations in the columns.

+     * 

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the default values differ

+     */

+    public static boolean isDefaultValueChanged(Column sourceColumn, Column targetColumn)

+    {

+        Object sourceDefaultValue = sourceColumn.getParsedDefaultValue();

+        Object targetDefaultValue = targetColumn.getParsedDefaultValue();

+

+        return ((sourceDefaultValue == null) && (targetDefaultValue != null)) ||

+               ((sourceDefaultValue != null) && !sourceDefaultValue.equals(targetDefaultValue));

+    }

+

+    /**

+     * Determines whether the required status of the given target column is different from that of the given source column.

+     * 

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the required status is different in the target column

+     */

+    public static boolean isRequiredStatusChanged(Column sourceColumn, Column targetColumn)

+    {

+        return sourceColumn.isRequired() != targetColumn.isRequired();

+    }

+

+    /**

+     * Determines whether the auto increment status of the given target column is different from that of the given source column.

+     * 

+     * @param sourceColumn The source column

+     * @param targetColumn The target column

+     * @return <code>true</code> if the auto increment status is different in the target column

+     */

+    public static boolean isAutoIncrementChanged(Column sourceColumn, Column targetColumn)

+    {

+        return sourceColumn.isAutoIncrement() != targetColumn.isAutoIncrement();

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void apply(Database model, boolean caseSensitive)

+    {

+        Column column = findChangedColumn(model, caseSensitive);

+

+        column.setTypeCode(_newColumnDef.getTypeCode());

+        column.setSize(_newColumnDef.getSize());

+        column.setAutoIncrement(_newColumnDef.isAutoIncrement());

+        column.setRequired(_newColumnDef.isRequired());

+        column.setDescription(_newColumnDef.getDescription());

+        column.setDefaultValue(_newColumnDef.getDefaultValue());

+    }

+}

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnOrderChange.java b/src/java/org/apache/ddlutils/alteration/ColumnOrderChange.java
index 520f9c2..1205cda 100644
--- a/src/java/org/apache/ddlutils/alteration/ColumnOrderChange.java
+++ b/src/java/org/apache/ddlutils/alteration/ColumnOrderChange.java
@@ -20,6 +20,7 @@
  */

 

 import java.util.ArrayList;

+import java.util.Iterator;

 import java.util.Map;

 

 import org.apache.ddlutils.model.Column;

@@ -39,24 +40,43 @@
     /**

      * Creates a new change object.

      * 

-     * @param table        The table whose primary key is to be changed

-     * @param newPositions The map containing the new positions keyed by the source columns

+     * @param tableName    The name of the table whose primary key is to be changed

+     * @param newPositions The map containing the new positions keyed by the source column names

      */

-    public ColumnOrderChange(Table table, Map newPositions)

+    public ColumnOrderChange(String tableName, Map newPositions)

     {

-        super(table);

+        super(tableName);

         _newPositions = newPositions;

     }

 

     /**

      * Returns the new position of the given source column.

      *

-     * @param sourceColumn The column

+     * @param sourceColumnName The column's name

+     * @param caseSensitive    Whether case of the column name matters

      * @return The new position or -1 if no position is marked for the column

      */

-    public int getNewPosition(Column sourceColumn)

+    public int getNewPosition(String sourceColumnName, boolean caseSensitive)

     {

-        Integer newPos = (Integer)_newPositions.get(sourceColumn);

+        Integer newPos = null;

+

+        if (caseSensitive)

+        {

+            newPos = (Integer)_newPositions.get(sourceColumnName);

+        }

+        else

+        {

+            for (Iterator it = _newPositions.entrySet().iterator(); it.hasNext();)

+            {

+                Map.Entry entry = (Map.Entry)it.next();

+

+                if (sourceColumnName.equalsIgnoreCase((String)entry.getKey()))

+                {

+                    newPos = (Integer)entry.getValue();

+                    break;

+                }

+            }

+        }

 

         return newPos == null ? -1 : newPos.intValue();

     }

@@ -66,20 +86,24 @@
      */

     public void apply(Database database, boolean caseSensitive)

     {

-        Table     table      = database.findTable(getChangedTable().getName(), caseSensitive);

-        ArrayList newColumns = new ArrayList(table.getColumnCount());

+        Table     table      = findChangedTable(database, caseSensitive);

+        ArrayList newColumns = new ArrayList();

 

         for (int idx = 0; idx < table.getColumnCount(); idx++)

         {

+             newColumns.add(table.getColumn(idx));

+        }        

+        for (int idx = 0; idx < table.getColumnCount(); idx++)

+        {

             Column column = table.getColumn(idx);

-            int    newPos = getNewPosition(column);

+            int    newPos = getNewPosition(column.getName(), caseSensitive);

 

-            newColumns.set(newPos < 0 ? idx : newPos, column);

+            if (newPos >= 0)

+            {

+                newColumns.set(newPos, column);

+            }

         }

-        for (int idx = 0; idx < table.getColumnCount(); idx++)

-        {

-            table.removeColumn(idx);

-        }

+        table.removeAllColumns();

         table.addColumns(newColumns);

     }

 }

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnRequiredChange.java b/src/java/org/apache/ddlutils/alteration/ColumnRequiredChange.java
deleted file mode 100644
index 276ffbf..0000000
--- a/src/java/org/apache/ddlutils/alteration/ColumnRequiredChange.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Represents the change of the required constraint of a column. Since it is a boolean value,

- * this means the required constraint will simply be toggled.

- * 

- * @version $Revision: $

- */

-public class ColumnRequiredChange extends ColumnChangeImplBase

-{

-    /**

-     * Creates a new change object.

-     * 

-     * @param table  The table of the column

-     * @param column The column

-     */

-    public ColumnRequiredChange(Table table, Column column)

-    {

-        super(table, column);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void apply(Database model, boolean caseSensitive)

-    {

-        findChangedColumn(model, caseSensitive).setRequired(!getChangedColumn().isRequired());

-    }

-}

diff --git a/src/java/org/apache/ddlutils/alteration/ColumnSizeChange.java b/src/java/org/apache/ddlutils/alteration/ColumnSizeChange.java
deleted file mode 100644
index 6727604..0000000
--- a/src/java/org/apache/ddlutils/alteration/ColumnSizeChange.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Represents the change of the size or scale of a column.

- * 

- * @version $Revision: $

- */

-public class ColumnSizeChange extends ColumnChangeImplBase

-{

-    /** The new size. */

-    private int _newSize;

-    /** The new scale. */

-    private int _newScale;

-

-    /**

-     * Creates a new change object.

-     * 

-     * @param table    The table of the column

-     * @param column   The column

-     * @param newSize  The new size

-     * @param newScale The new scale

-     */

-    public ColumnSizeChange(Table table, Column column, int newSize, int newScale)

-    {

-        super(table, column);

-        _newSize  = newSize;

-        _newScale = newScale;

-    }

-

-    /**

-     * Returns the new size of the column.

-     *

-     * @return The new size

-     */

-    public int getNewSize()

-    {

-        return _newSize;

-    }

-

-    /**

-     * Returns the new scale of the column.

-     *

-     * @return The new scale

-     */

-    public int getNewScale()

-    {

-        return _newScale;

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void apply(Database model, boolean caseSensitive)

-    {

-        findChangedColumn(model, caseSensitive).setSizeAndScale(_newSize, _newScale);

-    }

-}

diff --git a/src/java/org/apache/ddlutils/alteration/ForeignKeyChange.java b/src/java/org/apache/ddlutils/alteration/ForeignKeyChange.java
index 57c8304..8396899 100644
--- a/src/java/org/apache/ddlutils/alteration/ForeignKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/ForeignKeyChange.java
@@ -30,13 +30,6 @@
 public interface ForeignKeyChange extends TableChange

 {

     /**

-     * Returns the affected foreign key from the original model.

-     * 

-     * @return The affected foreign key

-     */

-    public ForeignKey getChangedForeignKey();

-

-    /**

      * Finds the foreign key object corresponding to the changed foreign key in the given database model.

      * 

      * @param model         The database model

diff --git a/src/java/org/apache/ddlutils/alteration/ForeignKeyChangeImplBase.java b/src/java/org/apache/ddlutils/alteration/ForeignKeyChangeImplBase.java
index 2955df5..ba85dde 100644
--- a/src/java/org/apache/ddlutils/alteration/ForeignKeyChangeImplBase.java
+++ b/src/java/org/apache/ddlutils/alteration/ForeignKeyChangeImplBase.java
@@ -1,33 +1,59 @@
 package org.apache.ddlutils.alteration;
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
+import org.apache.ddlutils.model.Reference;
 import org.apache.ddlutils.model.Table;
 
+/**
+ * The base class for changes affecting foreign keys.
+ * 
+ * @version $Revision: $
+ */
 public abstract class ForeignKeyChangeImplBase extends    TableChangeImplBase
                                                implements ForeignKeyChange
 {
-    /** The foreign key. */
-    private ForeignKey _foreignKey;
+    /** List of pairs of local and corresponding foreign column names that make up the foreign key. */
+    private List _referenceColumnNames = new ArrayList();
 
     /**
      * Creates a new change object.
      * 
-     * @param table      The table
-     * @param foreignKey The foreign key
+     * @param tableName  The name of the table that owns the foreign key
+     * @param foreignKey The foreign key; note that this change object will not maintain a reference
+     *                   to the foreign key object
      */
-    public ForeignKeyChangeImplBase(Table table, ForeignKey foreignKey)
+    public ForeignKeyChangeImplBase(String tableName, ForeignKey foreignKey)
     {
-        super(table);
-        _foreignKey = foreignKey;
-    }
+        super(tableName);
+        for (int refIdx = 0; refIdx < foreignKey.getReferenceCount(); refIdx++)
+        {
+            Reference ref = foreignKey.getReference(refIdx);
 
-    /**
-     * {@inheritDoc}
-     */
-    public ForeignKey getChangedForeignKey()
-    {
-        return _foreignKey;
+            _referenceColumnNames.add(new Pair(ref.getLocalColumnName(), ref.getForeignColumnName()));
+        }
     }
 
     /**
@@ -43,10 +69,30 @@
             {
                 ForeignKey curFk = table.getForeignKey(fkIdx);
 
-                if ((caseSensitive  && _foreignKey.equals(curFk)) ||
-                    (!caseSensitive && _foreignKey.equalsIgnoreCase(curFk)))
+                if (curFk.getReferenceCount() == _referenceColumnNames.size())
                 {
-                    return curFk;
+                    for (int refIdx = 0; refIdx < curFk.getReferenceCount(); refIdx++)
+                    {
+                        Reference ref      = curFk.getReference(refIdx);
+                        Pair      colNames = (Pair)_referenceColumnNames.get(refIdx);
+
+                        if (caseSensitive)
+                        {
+                            if (ref.getLocalColumnName().equals((String)colNames.getFirst()) &&
+                                ref.getForeignColumnName().equals((String)colNames.getSecond()))
+                            {
+                                return curFk;
+                            }
+                        }
+                        else
+                        {
+                            if (ref.getLocalColumnName().equalsIgnoreCase((String)colNames.getFirst()) &&
+                                ref.getForeignColumnName().equalsIgnoreCase((String)colNames.getSecond()))
+                            {
+                                return curFk;
+                            }
+                        }
+                    }
                 }
             }
     	}
diff --git a/src/java/org/apache/ddlutils/alteration/IndexChange.java b/src/java/org/apache/ddlutils/alteration/IndexChange.java
index 3d192b9..1b6069e 100644
--- a/src/java/org/apache/ddlutils/alteration/IndexChange.java
+++ b/src/java/org/apache/ddlutils/alteration/IndexChange.java
@@ -30,13 +30,6 @@
 public interface IndexChange extends TableChange

 {

     /**

-     * Returns the affected index from the original model.

-     * 

-     * @return The affected index

-     */

-    public Index getChangedIndex();

-

-    /**

      * Finds the index object corresponding to the changed index in the given database model.

      * 

      * @param model         The database model

diff --git a/src/java/org/apache/ddlutils/alteration/IndexChangeImplBase.java b/src/java/org/apache/ddlutils/alteration/IndexChangeImplBase.java
index 6d9475c..60bcedd 100644
--- a/src/java/org/apache/ddlutils/alteration/IndexChangeImplBase.java
+++ b/src/java/org/apache/ddlutils/alteration/IndexChangeImplBase.java
@@ -1,33 +1,56 @@
 package org.apache.ddlutils.alteration;
 
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.List;
+
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Index;
 import org.apache.ddlutils.model.Table;
 
+/**
+ * The base class for changes affecting indexes.
+ * 
+ * @version $Revision: $
+ */
 public abstract class IndexChangeImplBase extends    TableChangeImplBase
                                           implements IndexChange
 {
-    /** The index. */
-    private Index _index;
+    /** The names of the columns in the index. */
+    private List _columnNames = new ArrayList();
 
     /**
      * Creates a new change object.
      * 
-     * @param table The table
-     * @param index The index
+     * @param tableName The name of the changed table
+     * @param index     The index; note that this change object will not maintain a reference
+     *                  to the index object
      */
-    public IndexChangeImplBase(Table table, Index index)
+    public IndexChangeImplBase(String tableName, Index index)
     {
-        super(table);
-        _index = index;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    public Index getChangedIndex()
-    {
-        return _index;
+        super(tableName);
+        for (int colIdx = 0; colIdx < index.getColumnCount(); colIdx++)
+        {
+            _columnNames.add(index.getColumn(colIdx).getName());
+        }
     }
 
     /**
@@ -35,21 +58,30 @@
      */
     public Index findChangedIndex(Database model, boolean caseSensitive)
     {
-    	Table table = findChangedTable(model, caseSensitive);
+        Table table = findChangedTable(model, caseSensitive);
 
-    	if (table != null)
-    	{
+        if (table != null)
+        {
             for (int indexIdx = 0; indexIdx < table.getIndexCount(); indexIdx++)
             {
                 Index curIndex = table.getIndex(indexIdx);
 
-                if ((caseSensitive  && _index.equals(curIndex)) ||
-                    (!caseSensitive && _index.equalsIgnoreCase(curIndex)))
+                if (curIndex.getColumnCount() == _columnNames.size())
                 {
-                    return curIndex;
+                    for (int colIdx = 0; colIdx < curIndex.getColumnCount(); colIdx++)
+                    {
+                        String curColName      = curIndex.getColumn(colIdx).getName();
+                        String expectedColName = (String)_columnNames.get(colIdx);
+
+                        if ((caseSensitive  && curColName.equals(expectedColName)) ||
+                            (!caseSensitive && curColName.equalsIgnoreCase(expectedColName)))
+                        {
+                            return curIndex;
+                        }
+                    }
                 }
             }
-    	}
+        }
         return null;
     }
 }
diff --git a/src/java/org/apache/ddlutils/alteration/ModelComparator.java b/src/java/org/apache/ddlutils/alteration/ModelComparator.java
index e208f6a..1b4972e 100644
--- a/src/java/org/apache/ddlutils/alteration/ModelComparator.java
+++ b/src/java/org/apache/ddlutils/alteration/ModelComparator.java
@@ -23,15 +23,16 @@
 import java.util.HashMap;

 import java.util.List;

 

-import org.apache.commons.lang.StringUtils;

 import org.apache.commons.logging.Log;

 import org.apache.commons.logging.LogFactory;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.model.CloneHelper;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.ForeignKey;

 import org.apache.ddlutils.model.Index;

 import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.util.StringUtils;

 

 /**

  * Compares two database models and creates change objects that express how to

@@ -39,8 +40,6 @@
  * are changed in the process, however, it is also assumed that the models do not

  * change in between.

  * 

- * TODO: Add support and tests for the change of the column order 

- * 

  * @version $Revision: $

  */

 public class ModelComparator

@@ -50,19 +49,79 @@
 

     /** The platform information. */

     private PlatformInfo _platformInfo;

+    /** The predicate that defines which changes are supported by the platform. */

+    private TableDefinitionChangesPredicate _tableDefCangePredicate;

+    /** The object clone helper. */

+    private CloneHelper _cloneHelper = new CloneHelper();

     /** Whether comparison is case sensitive. */

     private boolean _caseSensitive;

+    /** Whether the comparator should generate {@link PrimaryKeyChange} objects. */

+    private boolean _generatePrimaryKeyChanges = true;

+    /** Whether {@link RemoveColumnChange} objects for primary key columns are enough or

+        additional primary key change objects are necessary. */

+    private boolean _canDropPrimaryKeyColumns = true;

 

     /**

      * Creates a new model comparator object.

      * 

-     * @param platformInfo  The platform info

-     * @param caseSensitive Whether comparison is case sensitive

+     * @param platformInfo            The platform info

+     * @param tableDefChangePredicate The predicate that defines whether tables changes are supported

+     *                                by the platform or not; all changes are supported if this is null

+     * @param caseSensitive           Whether comparison is case sensitive

      */

-    public ModelComparator(PlatformInfo platformInfo, boolean caseSensitive)

+    public ModelComparator(PlatformInfo                    platformInfo,

+                           TableDefinitionChangesPredicate tableDefChangePredicate,

+                           boolean                         caseSensitive)

     {

-        _platformInfo  = platformInfo;

-        _caseSensitive = caseSensitive;

+        _platformInfo           = platformInfo;

+        _caseSensitive          = caseSensitive;

+        _tableDefCangePredicate = tableDefChangePredicate;

+    }

+

+    /**

+     * Specifies whether the comparator should generate {@link PrimaryKeyChange} objects or a

+     * pair of {@link RemovePrimaryKeyChange} and {@link AddPrimaryKeyChange} objects instead.

+     * The default value is <code>true</code>.

+     * 

+     * @param generatePrimaryKeyChanges Whether to create {@link PrimaryKeyChange} objects

+     */

+    public void setGeneratePrimaryKeyChanges(boolean generatePrimaryKeyChanges)

+    {

+        _generatePrimaryKeyChanges = generatePrimaryKeyChanges;

+    }

+

+    /**

+     * Specifies whether the {@link RemoveColumnChange} are fine even for primary key columns.

+     * If the platform cannot drop primary key columns, set this to <code>false</code> and the

+     * comparator will create additional primary key changes. 

+     * The default value is <code>true</code>.

+     * 

+     * @param canDropPrimaryKeyColumns Whether {@link RemoveColumnChange} objecs for primary

+     *                                 key columns are ok 

+     */

+    public void setCanDropPrimaryKeyColumns(boolean canDropPrimaryKeyColumns)

+    {

+        _canDropPrimaryKeyColumns = canDropPrimaryKeyColumns;

+    }

+

+    /**

+     * Returns the info object for the platform.

+     * 

+     * @return The platform info object

+     */

+    protected PlatformInfo getPlatformInfo()

+    {

+        return _platformInfo;

+    }

+

+    /**

+     * Determines whether comparison should be case sensitive.

+     * 

+     * @return <code>true</code> if case matters

+     */

+    protected boolean isCaseSensitive()

+    {

+        return _caseSensitive;

     }

 

     /**

@@ -75,53 +134,208 @@
      */

     public List compare(Database sourceModel, Database targetModel)

     {

+        Database intermediateModel = _cloneHelper.clone(sourceModel);

+

+        return compareModels(sourceModel, intermediateModel, targetModel);

+    }

+

+    /**

+     * Compares the given source and target models and creates change objects to get from

+     * the source to the target one. These changes will be applied to the given

+     * intermediate model (the other two won't be changed), so that it will be equal to

+     * the target model after this model has finished.

+     * 

+     * @param sourceModel       The source model

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param targetModel       The target model

+     * @return The changes

+     */

+    protected List compareModels(Database sourceModel,

+                                 Database intermediateModel,

+                                 Database targetModel)

+    {

         ArrayList changes = new ArrayList();

 

+        changes.addAll(checkForRemovedForeignKeys(sourceModel, intermediateModel, targetModel));

+        changes.addAll(checkForRemovedTables(sourceModel, intermediateModel, targetModel));

+

+        for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++)

+        {

+            Table intermediateTable = intermediateModel.getTable(tableIdx);

+            Table sourceTable       = sourceModel.findTable(intermediateTable.getName(), _caseSensitive);

+            Table targetTable       = targetModel.findTable(intermediateTable.getName(), _caseSensitive);

+            List  tableChanges      = compareTables(sourceModel, sourceTable,

+                                                    intermediateModel, intermediateTable,

+                                                    targetModel, targetTable);

+

+            changes.addAll(tableChanges);

+        }

+

+        changes.addAll(checkForAddedTables(sourceModel, intermediateModel, targetModel));

+        changes.addAll(checkForAddedForeignKeys(sourceModel, intermediateModel, targetModel));

+        return changes;

+    }

+

+    /**

+     * Creates change objects for foreign keys that are present in the given source model but are no longer in the target

+     * model, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param targetModel       The target model

+     * @return The changes

+     */

+    protected List checkForRemovedForeignKeys(Database sourceModel,

+                                              Database intermediateModel,

+                                              Database targetModel)

+    {

+        List changes = new ArrayList();

+

+        for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++)

+        {

+            Table        intermediateTable = intermediateModel.getTable(tableIdx);

+            Table        targetTable       = targetModel.findTable(intermediateTable.getName(), _caseSensitive);

+            ForeignKey[] intermediateFks   = intermediateTable.getForeignKeys();

+

+            // Dropping foreign keys from tables to be removed might not be necessary, but some databases might require it

+            for (int fkIdx = 0; fkIdx < intermediateFks.length; fkIdx++)

+            {

+                ForeignKey sourceFk = intermediateFks[fkIdx];

+                ForeignKey targetFk = targetTable == null ? null : findCorrespondingForeignKey(targetTable, sourceFk);

+

+                if (targetFk == null)

+                {

+                    if (_log.isInfoEnabled())

+                    {

+                        _log.info("Foreign key " + sourceFk + " needs to be removed from table " + intermediateTable.getName());

+                    }

+

+                    RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(intermediateTable.getName(), sourceFk);

+

+                    changes.add(fkChange);

+                    fkChange.apply(intermediateModel, _caseSensitive);

+                }

+            }

+        }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for foreign keys that are not present in the given source model but are in the target

+     * model, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param targetModel       The target model

+     * @return The changes

+     */

+    protected List checkForAddedForeignKeys(Database sourceModel,

+                                            Database intermediateModel,

+                                            Database targetModel)

+    {

+        List changes = new ArrayList();

+

         for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++)

         {

-            Table targetTable = targetModel.getTable(tableIdx);

-            Table sourceTable = sourceModel.findTable(targetTable.getName(), _caseSensitive);

+            Table targetTable       = targetModel.getTable(tableIdx);

+            Table intermediateTable = intermediateModel.findTable(targetTable.getName(), _caseSensitive);

 

-            if (sourceTable == null)

+            for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)

+            {

+                ForeignKey targetFk       = targetTable.getForeignKey(fkIdx);

+                ForeignKey intermediateFk = findCorrespondingForeignKey(intermediateTable, targetFk);

+

+                if (intermediateFk == null)

+                {

+                    if (_log.isInfoEnabled())

+                    {

+                        _log.info("Foreign key " + targetFk + " needs to be added to table " + intermediateTable.getName());

+                    }

+

+                    intermediateFk = _cloneHelper.clone(targetFk, intermediateTable, intermediateModel, _caseSensitive);

+

+                    AddForeignKeyChange fkChange = new AddForeignKeyChange(intermediateTable.getName(), intermediateFk);

+

+                    changes.add(fkChange);

+                    fkChange.apply(intermediateModel, _caseSensitive);

+                }

+            }

+        }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for tables that are present in the given source model but are no longer in the target

+     * model, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param targetModel       The target model

+     * @return The changes

+     */

+    protected List checkForRemovedTables(Database sourceModel,

+                                         Database intermediateModel,

+                                         Database targetModel)

+    {

+        List    changes            = new ArrayList();

+        Table[] intermediateTables = intermediateModel.getTables();

+

+        for (int tableIdx = 0; tableIdx < intermediateTables.length; tableIdx++)

+        {

+            Table intermediateTable = intermediateTables[tableIdx];

+            Table targetTable       = targetModel.findTable(intermediateTable.getName(), _caseSensitive);

+

+            if (targetTable == null)

+            {

+                if (_log.isInfoEnabled())

+                {

+                    _log.info("Table " + intermediateTable.getName() + " needs to be removed");

+                }

+

+                RemoveTableChange tableChange = new RemoveTableChange(intermediateTable.getName());

+

+                changes.add(tableChange);

+                tableChange.apply(intermediateModel, _caseSensitive);

+            }

+        }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for tables that are not present in the given source model but are in the target

+     * model, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param targetModel       The target model

+     * @return The changes

+     */

+    protected List checkForAddedTables(Database sourceModel,

+                                       Database intermediateModel,

+                                       Database targetModel)

+    {

+        List changes = new ArrayList();

+

+        for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++)

+        {

+            Table targetTable       = targetModel.getTable(tableIdx);

+            Table intermediateTable = intermediateModel.findTable(targetTable.getName(), _caseSensitive);

+

+            if (intermediateTable == null)

             {

                 if (_log.isInfoEnabled())

                 {

                     _log.info("Table " + targetTable.getName() + " needs to be added");

                 }

-                changes.add(new AddTableChange(targetTable));

-                for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)

-                {

-                    // we have to use target table's definition here because the

-                    // complete table is new

-                    changes.add(new AddForeignKeyChange(targetTable, targetTable.getForeignKey(fkIdx)));

-                }

-            }

-            else

-            {

-                changes.addAll(compareTables(sourceModel, sourceTable, targetModel, targetTable));

-            }

-        }

 

-        for (int tableIdx = 0; tableIdx < sourceModel.getTableCount(); tableIdx++)

-        {

-            Table sourceTable = sourceModel.getTable(tableIdx);

-            Table targetTable = targetModel.findTable(sourceTable.getName(), _caseSensitive);

+                // we're using a clone of the target table, and remove all foreign

+                // keys as these will be added later

+                intermediateTable = _cloneHelper.clone(targetTable, true, false, intermediateModel, _caseSensitive);

 

-            if ((targetTable == null) && (sourceTable.getName() != null) && (sourceTable.getName().length() > 0))

-            {

-                if (_log.isInfoEnabled())

-                {

-                    _log.info("Table " + sourceTable.getName() + " needs to be removed");

-                }

-                changes.add(new RemoveTableChange(sourceTable));

-                // we assume that the target model is sound, ie. that there are no longer any foreign

-                // keys to this table in the target model; thus we already have removeFK changes for

-                // these from the compareTables method and we only need to create changes for the fks

-                // originating from this table

-                for (int fkIdx = 0; fkIdx < sourceTable.getForeignKeyCount(); fkIdx++)

-                {

-                    changes.add(new RemoveForeignKeyChange(sourceTable, sourceTable.getForeignKey(fkIdx)));

-                }

+                AddTableChange tableChange = new AddTableChange(intermediateTable);

+

+                changes.add(tableChange);

+                tableChange.apply(intermediateModel, _caseSensitive);

             }

         }

         return changes;

@@ -131,161 +345,450 @@
      * Compares the two tables and returns the changes necessary to create the second

      * table from the first one.

      *  

-     * @param sourceModel The source model which contains the source table

-     * @param sourceTable The source table

-     * @param targetModel The target model which contains the target table

-     * @param targetTable The target table

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to which the changes will be applied incrementally

+     * @param intermediateTable The table corresponding to the source table in the intermediate model

+     * @param targetModel       The target model which contains the target table

+     * @param targetTable       The target table

      * @return The changes

      */

-    public List compareTables(Database sourceModel,

-                              Table    sourceTable,

-                              Database targetModel,

-                              Table    targetTable)

+    protected List compareTables(Database sourceModel,

+                                 Table    sourceTable,

+                                 Database intermediateModel,

+                                 Table    intermediateTable,

+                                 Database targetModel,

+                                 Table    targetTable)

     {

         ArrayList changes = new ArrayList();

 

-        for (int fkIdx = 0; fkIdx < sourceTable.getForeignKeyCount(); fkIdx++)

-        {

-            ForeignKey sourceFk = sourceTable.getForeignKey(fkIdx);

-            ForeignKey targetFk = findCorrespondingForeignKey(targetTable, sourceFk);

+        changes.addAll(checkForRemovedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

 

-            if (targetFk == null)

+        ArrayList tableDefinitionChanges = new ArrayList();

+        Table     tmpTable               = _cloneHelper.clone(intermediateTable, true, false, intermediateModel, _caseSensitive);

+

+        tableDefinitionChanges.addAll(checkForRemovedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

+        tableDefinitionChanges.addAll(checkForChangeOfColumnOrder(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

+        tableDefinitionChanges.addAll(checkForChangedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

+        tableDefinitionChanges.addAll(checkForAddedColumns(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

+        tableDefinitionChanges.addAll(checkForPrimaryKeyChanges(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

+

+        if (!tableDefinitionChanges.isEmpty())

+        {

+            if ((_tableDefCangePredicate == null) || _tableDefCangePredicate.areSupported(tmpTable, tableDefinitionChanges))

             {

-                if (_log.isInfoEnabled())

+                changes.addAll(tableDefinitionChanges);

+            }

+            else

+            {

+                // we need to recreate the table; for this to work we need to remove foreign keys to and from the table

+                // however, we don't have to add them back here as there is a check for added foreign keys/indexes

+                // later on anyways

+                // we also don't have to drop indexes on the original table

+

+                ForeignKey[] fks = intermediateTable.getForeignKeys();

+

+                for (int fkIdx = 0; fkIdx < fks.length; fkIdx++)

                 {

-                    _log.info("Foreign key " + sourceFk + " needs to be removed from table " + sourceTable.getName());

+                    RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(intermediateTable.getName(), fks[fkIdx]);

+

+                    changes.add(fkChange);

+                    fkChange.apply(intermediateModel, _caseSensitive);

                 }

-                changes.add(new RemoveForeignKeyChange(sourceTable, sourceFk));

+                for (int tableIdx = 0; tableIdx < intermediateModel.getTableCount(); tableIdx++)

+                {

+                    Table curTable = intermediateModel.getTable(tableIdx);

+

+                    if (curTable != intermediateTable)

+                    {

+                        ForeignKey[] curFks = curTable.getForeignKeys();

+

+                        for (int fkIdx = 0; fkIdx < curFks.length; fkIdx++)

+                        {

+                            if ((_caseSensitive  && curFks[fkIdx].getForeignTableName().equals(intermediateTable.getName())) ||

+                                (!_caseSensitive && curFks[fkIdx].getForeignTableName().equalsIgnoreCase(intermediateTable.getName())))

+                            {

+                                RemoveForeignKeyChange fkChange = new RemoveForeignKeyChange(curTable.getName(), curFks[fkIdx]);

+    

+                                changes.add(fkChange);

+                                fkChange.apply(intermediateModel, _caseSensitive);

+                            }

+                        }

+                    }

+                }

+

+                RecreateTableChange tableChange =  new RecreateTableChange(intermediateTable.getName(),

+                                                                           intermediateTable,

+                                                                           new ArrayList(tableDefinitionChanges));

+

+                changes.add(tableChange);

+                tableChange.apply(intermediateModel, _caseSensitive);

             }

         }

+        

+        changes.addAll(checkForAddedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable));

 

-        for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)

+        return changes;

+    }

+

+    /**

+     * Returns the names of the columns in the intermediate table corresponding to the given column objects.

+     * 

+     * @param columns           The column objects

+     * @param intermediateTable The intermediate table

+     * @return The column names

+     */

+    protected String[] getIntermediateColumnNamesFor(Column[] columns, Table intermediateTable)

+    {

+        String[] result = new String[columns.length];

+

+        for (int idx = 0; idx < columns.length; idx++)

         {

-            ForeignKey targetFk = targetTable.getForeignKey(fkIdx);

-            ForeignKey sourceFk = findCorrespondingForeignKey(sourceTable, targetFk);

-

-            if (sourceFk == null)

-            {

-                if (_log.isInfoEnabled())

-                {

-                    _log.info("Foreign key " + targetFk + " needs to be created for table " + sourceTable.getName());

-                }

-                // we have to use the target table here because the foreign key might

-                // reference a new column

-                changes.add(new AddForeignKeyChange(targetTable, targetFk));

-            }

+            result[idx] = intermediateTable.findColumn(columns[idx].getName(), _caseSensitive).getName();

         }

+        return result;

+    }

 

-        for (int indexIdx = 0; indexIdx < sourceTable.getIndexCount(); indexIdx++)

+    /**

+     * Creates change objects for indexes that are present in the given source table but are no longer in the target

+     * table, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForRemovedIndexes(Database sourceModel,

+                                          Table    sourceTable,

+                                          Database intermediateModel,

+                                          Table    intermediateTable,

+                                          Database targetModel,

+                                          Table    targetTable)

+    {

+        List    changes = new ArrayList();

+        Index[] indexes = intermediateTable.getIndices();

+

+        for (int indexIdx = 0; indexIdx < indexes.length; indexIdx++)

         {

-            Index sourceIndex = sourceTable.getIndex(indexIdx);

+            Index sourceIndex = indexes[indexIdx];

             Index targetIndex = findCorrespondingIndex(targetTable, sourceIndex);

 

             if (targetIndex == null)

             {

                 if (_log.isInfoEnabled())

                 {

-                    _log.info("Index " + sourceIndex.getName() + " needs to be removed from table " + sourceTable.getName());

+                    _log.info("Index " + sourceIndex.getName() + " needs to be removed from table " + intermediateTable.getName());

                 }

-                changes.add(new RemoveIndexChange(sourceTable, sourceIndex));

+

+                RemoveIndexChange change = new RemoveIndexChange(intermediateTable.getName(), sourceIndex);

+

+                changes.add(change);

+                change.apply(intermediateModel, _caseSensitive);

             }

         }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for indexes that are not present in the given source table but are in the target

+     * table, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForAddedIndexes(Database sourceModel,

+                                        Table    sourceTable,

+                                        Database intermediateModel,

+                                        Table    intermediateTable,

+                                        Database targetModel,

+                                        Table    targetTable)

+    {

+        List changes = new ArrayList();

+

         for (int indexIdx = 0; indexIdx < targetTable.getIndexCount(); indexIdx++)

         {

             Index targetIndex = targetTable.getIndex(indexIdx);

-            Index sourceIndex = findCorrespondingIndex(sourceTable, targetIndex);

+            Index sourceIndex = findCorrespondingIndex(intermediateTable, targetIndex);

 

             if (sourceIndex == null)

             {

                 if (_log.isInfoEnabled())

                 {

-                    _log.info("Index " + targetIndex.getName() + " needs to be created for table " + sourceTable.getName());

+                    _log.info("Index " + targetIndex.getName() + " needs to be created for table " + intermediateTable.getName());

                 }

-                // we have to use the target table here because the index might

-                // reference a new column

-                changes.add(new AddIndexChange(targetTable, targetIndex));

+

+                Index          clonedIndex = _cloneHelper.clone(targetIndex, intermediateTable, _caseSensitive);

+                AddIndexChange change      = new AddIndexChange(intermediateTable.getName(), clonedIndex);

+

+                changes.add(change);

+                change.apply(intermediateModel, _caseSensitive);

             }

         }

+        return changes;

+    }

 

-        HashMap addColumnChanges = new HashMap();

+    /**

+     * Checks for changes in the column order between the given source and target table, creates change objects for these and

+     * applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForChangeOfColumnOrder(Database sourceModel,

+                                               Table    sourceTable,

+                                               Database intermediateModel,

+                                               Table    intermediateTable,

+                                               Database targetModel,

+                                               Table    targetTable)

+    {

+        List changes       = new ArrayList();

+        List targetOrder   = new ArrayList();

+        int  numChangedPKs = 0;

 

         for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++)

         {

             Column targetColumn = targetTable.getColumn(columnIdx);

-            Column sourceColumn = sourceTable.findColumn(targetColumn.getName(), _caseSensitive);

+            Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive);

 

-            if (sourceColumn == null)

+            if (sourceColumn != null)

+            {

+                targetOrder.add(sourceColumn);

+            }

+        }

+

+        HashMap newPositions = new HashMap();

+

+        for (int columnIdx = 0; columnIdx < intermediateTable.getColumnCount(); columnIdx++)

+        {

+            Column sourceColumn = intermediateTable.getColumn(columnIdx);

+            int    targetIdx    = targetOrder.indexOf(sourceColumn);

+

+            if ((targetIdx >= 0) && (targetIdx != columnIdx))

+            {

+                newPositions.put(sourceColumn.getName(), new Integer(targetIdx));

+                if (sourceColumn.isPrimaryKey())

+                {

+                    numChangedPKs++;

+                }

+            }

+        }

+

+        if (!newPositions.isEmpty())

+        {

+            ColumnOrderChange change = new ColumnOrderChange(intermediateTable.getName(), newPositions);

+

+            change.apply(intermediateModel, _caseSensitive);

+            if (numChangedPKs > 1)

+            {

+                // create pk change that only covers the order change

+                // fortunately, the order change will have adjusted the pk order already

+                changes.add(new PrimaryKeyChange(intermediateTable.getName(),

+                                                 getIntermediateColumnNamesFor(intermediateTable.getPrimaryKeyColumns(), intermediateTable)));

+            }

+            changes.add(change);

+        }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for columns that are present in the given source table but are no longer in the target

+     * table, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForRemovedColumns(Database sourceModel,

+                                          Table    sourceTable,

+                                          Database intermediateModel,

+                                          Table    intermediateTable,

+                                          Database targetModel,

+                                          Table    targetTable)

+    {

+        // if the platform does not support dropping pk columns, then the pk handling above will

+        // generate appropriate pk changes

+

+        List     changes = new ArrayList();

+        Column[] columns = intermediateTable.getColumns();

+

+        for (int columnIdx = 0; columnIdx < columns.length; columnIdx++)

+        {

+            Column sourceColumn = columns[columnIdx];

+            Column targetColumn = targetTable.findColumn(sourceColumn.getName(), _caseSensitive);

+

+            if (targetColumn == null)

             {

                 if (_log.isInfoEnabled())

                 {

-                    _log.info("Column " + targetColumn.getName() + " needs to be created for table " + sourceTable.getName());

+                    _log.info("Column " + sourceColumn.getName() + " needs to be removed from table " + intermediateTable.getName());

                 }

 

-                AddColumnChange change = new AddColumnChange(sourceTable,

-                                                             targetColumn,

-                                                             columnIdx > 0 ? targetTable.getColumn(columnIdx - 1) : null,

-                                                             columnIdx < targetTable.getColumnCount() - 1 ? targetTable.getColumn(columnIdx + 1) : null);

+                RemoveColumnChange change = new RemoveColumnChange(intermediateTable.getName(), sourceColumn.getName());

 

                 changes.add(change);

-                addColumnChanges.put(targetColumn, change);

-            }

-            else

-            {

-                changes.addAll(compareColumns(sourceTable, sourceColumn, targetTable, targetColumn));

+                change.apply(intermediateModel, _caseSensitive);

             }

         }

-        // if the last columns in the target table are added, then we note this at the changes

-        for (int columnIdx = targetTable.getColumnCount() - 1; columnIdx >= 0; columnIdx--)

+        return changes;

+    }

+

+    /**

+     * Creates change objects for columns that are not present in the given source table but are in the target

+     * table, and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForAddedColumns(Database sourceModel,

+                                        Table    sourceTable,

+                                        Database intermediateModel,

+                                        Table    intermediateTable,

+                                        Database targetModel,

+                                        Table    targetTable)

+    {

+        List changes = new ArrayList();

+

+        for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++)

         {

-            Column          targetColumn = targetTable.getColumn(columnIdx);

-            AddColumnChange change       = (AddColumnChange)addColumnChanges.get(targetColumn);

+            Column targetColumn = targetTable.getColumn(columnIdx);

+            Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive);

 

-            if (change == null)

+            if (sourceColumn == null)

             {

-                // column was not added, so we can ignore any columns before it that were added

-                break;

-            }

-            else

-            {

-                change.setAtEnd(true);

+                String          prevColumn   = (columnIdx > 0 ? intermediateTable.getColumn(columnIdx - 1).getName() : null);

+                String          nextColumn   = (columnIdx < intermediateTable.getColumnCount()  ? intermediateTable.getColumn(columnIdx).getName() : null);

+                Column          clonedColumn = _cloneHelper.clone(targetColumn, false);

+                AddColumnChange change       = new AddColumnChange(intermediateTable.getName(), clonedColumn, prevColumn, nextColumn);

+

+                changes.add(change);

+                change.apply(intermediateModel, _caseSensitive);

             }

         }

+        return changes;

+    }

 

+    /**

+     * Creates change objects for columns that have a different in the given source and target table, and applies them

+     * to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForChangedColumns(Database sourceModel,

+                                          Table    sourceTable,

+                                          Database intermediateModel,

+                                          Table    intermediateTable,

+                                          Database targetModel,

+                                          Table    targetTable)

+    {

+        List changes = new ArrayList();

+

+        for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++)

+        {

+            Column targetColumn = targetTable.getColumn(columnIdx);

+            Column sourceColumn = intermediateTable.findColumn(targetColumn.getName(), _caseSensitive);

+

+            if (sourceColumn != null)

+            {

+                ColumnDefinitionChange change = compareColumns(intermediateTable, sourceColumn, targetTable, targetColumn);

+

+                if (change != null)

+                {

+                    changes.add(change);

+                    change.apply(intermediateModel, _caseSensitive);

+                }

+            }

+        }

+        return changes;

+    }

+

+    /**

+     * Creates change objects for primary key differences (primary key added/removed/changed), and applies them to the given intermediate model.

+     * 

+     * @param sourceModel       The source model

+     * @param sourceTable       The source table

+     * @param intermediateModel The intermediate model to apply the changes to

+     * @param intermediateTable The table from the intermediate model corresponding to the source table

+     * @param targetModel       The target model

+     * @param targetTable       The target table

+     * @return The changes

+     */

+    protected List checkForPrimaryKeyChanges(Database sourceModel,

+                                             Table    sourceTable,

+                                             Database intermediateModel,

+                                             Table    intermediateTable,

+                                             Database targetModel,

+                                             Table    targetTable)

+    {

+        List     changes  = new ArrayList();

         Column[] sourcePK = sourceTable.getPrimaryKeyColumns();

+        Column[] curPK    = intermediateTable.getPrimaryKeyColumns();

         Column[] targetPK = targetTable.getPrimaryKeyColumns();

 

-        if ((sourcePK.length == 0) && (targetPK.length > 0))

+        if ((curPK.length == 0) && (targetPK.length > 0))

         {

             if (_log.isInfoEnabled())

             {

-                _log.info("A primary key needs to be added to the table " + sourceTable.getName());

+                _log.info("A primary key needs to be added to the table " + intermediateTable.getName());

             }

-            // we have to use the target table here because the primary key might

-            // reference a new column

-            changes.add(new AddPrimaryKeyChange(targetTable, targetPK));

+

+            AddPrimaryKeyChange change = new AddPrimaryKeyChange(intermediateTable.getName(), getIntermediateColumnNamesFor(targetPK, intermediateTable));

+

+            changes.add(change);

+            change.apply(intermediateModel, _caseSensitive);

         }

-        else if ((targetPK.length == 0) && (sourcePK.length > 0))

+        else if ((targetPK.length == 0) && (curPK.length > 0))

         {

             if (_log.isInfoEnabled())

             {

-                _log.info("The primary key needs to be removed from the table " + sourceTable.getName());

+                _log.info("The primary key needs to be removed from the table " + intermediateTable.getName());

             }

-            changes.add(new RemovePrimaryKeyChange(sourceTable, sourcePK));

+

+            RemovePrimaryKeyChange change = new RemovePrimaryKeyChange(intermediateTable.getName());

+

+            changes.add(change);

+            change.apply(intermediateModel, _caseSensitive);

         }

-        else if ((sourcePK.length > 0) && (targetPK.length > 0))

+        else

         {

             boolean changePK = false;

 

-            if (sourcePK.length != targetPK.length)

+            if ((curPK.length != targetPK.length) || (!_canDropPrimaryKeyColumns && sourcePK.length > targetPK.length))

             {

                 changePK = true;

             }

-            else

+            else if ((curPK.length > 0) && (targetPK.length > 0))

             {

-                for (int pkColumnIdx = 0; (pkColumnIdx < sourcePK.length) && !changePK; pkColumnIdx++)

+                for (int pkColumnIdx = 0; (pkColumnIdx < curPK.length) && !changePK; pkColumnIdx++)

                 {

-                    if ((_caseSensitive  && !sourcePK[pkColumnIdx].getName().equals(targetPK[pkColumnIdx].getName())) ||

-                        (!_caseSensitive && !sourcePK[pkColumnIdx].getName().equalsIgnoreCase(targetPK[pkColumnIdx].getName())))

+                    if (!StringUtils.equals(curPK[pkColumnIdx].getName(), targetPK[pkColumnIdx].getName(), _caseSensitive))

                     {

                         changePK = true;

                     }

@@ -295,101 +798,66 @@
             {

                 if (_log.isInfoEnabled())

                 {

-                    _log.info("The primary key of table " + sourceTable.getName() + " needs to be changed");

+                    _log.info("The primary key of table " + intermediateTable.getName() + " needs to be changed");

                 }

-                changes.add(new PrimaryKeyChange(sourceTable, targetPK));

-            }

-        }

-        

-        HashMap columnPosChanges = new HashMap();

-

-        for (int columnIdx = 0; columnIdx < sourceTable.getColumnCount(); columnIdx++)

-        {

-            Column sourceColumn = sourceTable.getColumn(columnIdx);

-            Column targetColumn = targetTable.findColumn(sourceColumn.getName(), _caseSensitive);

-

-            if (targetColumn == null)

-            {

-                if (_log.isInfoEnabled())

+                if (_generatePrimaryKeyChanges)

                 {

-                    _log.info("Column " + sourceColumn.getName() + " needs to be removed from table " + sourceTable.getName());

+                    PrimaryKeyChange change = new PrimaryKeyChange(intermediateTable.getName(),

+                                                                   getIntermediateColumnNamesFor(targetPK, intermediateTable));

+    

+                    changes.add(change);

+                    change.apply(intermediateModel, changePK);

                 }

-                changes.add(new RemoveColumnChange(sourceTable, sourceColumn));

-            }

-            else

-            {

-                int targetColumnIdx = targetTable.getColumnIndex(targetColumn);

-

-                if (targetColumnIdx != columnIdx)

+                else

                 {

-                    columnPosChanges.put(sourceColumn, new Integer(targetColumnIdx));

+                    RemovePrimaryKeyChange removePKChange = new RemovePrimaryKeyChange(intermediateTable.getName());

+                    AddPrimaryKeyChange    addPKChange    = new AddPrimaryKeyChange(intermediateTable.getName(),

+                                                                                    getIntermediateColumnNamesFor(targetPK, intermediateTable));

+

+                    changes.add(removePKChange);

+                    changes.add(addPKChange);

+                    removePKChange.apply(intermediateModel, _caseSensitive);

+                    addPKChange.apply(intermediateModel, _caseSensitive);

                 }

             }

         }

-        if (!columnPosChanges.isEmpty())

-        {

-            changes.add(new ColumnOrderChange(sourceTable, columnPosChanges));

-        }

-

         return changes;

     }

 

     /**

-     * Compares the two columns and returns the changes necessary to create the second

-     * column from the first one.

+     * Compares the two columns and returns the change necessary to create the second

+     * column from the first one if they differe.

      *  

      * @param sourceTable  The source table which contains the source column

      * @param sourceColumn The source column

      * @param targetTable  The target table which contains the target column

      * @param targetColumn The target column

-     * @return The changes

+     * @return The change or <code>null</code> if the columns are the same

      */

-    public List compareColumns(Table  sourceTable,

-                               Column sourceColumn,

-                               Table  targetTable,

-                               Column targetColumn)

+    protected ColumnDefinitionChange compareColumns(Table  sourceTable,

+                                                    Column sourceColumn,

+                                                    Table  targetTable,

+                                                    Column targetColumn)

     {

-        ArrayList changes = new ArrayList();

-

-        if (_platformInfo.getTargetJdbcType(targetColumn.getTypeCode()) != sourceColumn.getTypeCode())

+        if (ColumnDefinitionChange.isChanged(getPlatformInfo(), sourceColumn, targetColumn))

         {

-            changes.add(new ColumnDataTypeChange(sourceTable, sourceColumn, targetColumn.getTypeCode()));

+            Column  newColumnDef   = _cloneHelper.clone(sourceColumn, true);

+            int     targetTypeCode = _platformInfo.getTargetJdbcType(targetColumn.getTypeCode());

+            boolean sizeMatters    = _platformInfo.hasSize(targetTypeCode);

+            boolean scaleMatters   = _platformInfo.hasPrecisionAndScale(targetTypeCode);

+

+            newColumnDef.setTypeCode(targetColumn.getTypeCode());

+            newColumnDef.setSize(sizeMatters || scaleMatters ? targetColumn.getSize() : null);

+            newColumnDef.setAutoIncrement(targetColumn.isAutoIncrement());

+            newColumnDef.setRequired(targetColumn.isRequired());

+            newColumnDef.setDescription(targetColumn.getDescription());

+            newColumnDef.setDefaultValue(targetColumn.getDefaultValue());

+            return new ColumnDefinitionChange(sourceTable.getName(), sourceColumn.getName(), newColumnDef);

         }

-

-        boolean sizeMatters  = _platformInfo.hasSize(sourceColumn.getTypeCode());

-        boolean scaleMatters = _platformInfo.hasPrecisionAndScale(sourceColumn.getTypeCode());

-

-        if (sizeMatters &&

-            !StringUtils.equals(sourceColumn.getSize(), targetColumn.getSize()))

+        else

         {

-            changes.add(new ColumnSizeChange(sourceTable, sourceColumn, targetColumn.getSizeAsInt(), targetColumn.getScale()));

+            return null;

         }

-        else if (scaleMatters &&

-            (!StringUtils.equals(sourceColumn.getSize(), targetColumn.getSize()) ||

-             (sourceColumn.getScale() != targetColumn.getScale())))

-        {

-            changes.add(new ColumnSizeChange(sourceTable, sourceColumn, targetColumn.getSizeAsInt(), targetColumn.getScale()));

-        }

-

-        Object sourceDefaultValue = sourceColumn.getParsedDefaultValue();

-        Object targetDefaultValue = targetColumn.getParsedDefaultValue();

-

-        if (((sourceDefaultValue == null) && (targetDefaultValue != null)) ||

-            ((sourceDefaultValue != null) && !sourceDefaultValue.equals(targetDefaultValue)))

-        {

-            changes.add(new ColumnDefaultValueChange(sourceTable, sourceColumn, targetColumn.getDefaultValue()));

-        }

-

-        if (sourceColumn.isRequired() != targetColumn.isRequired())

-        {

-            changes.add(new ColumnRequiredChange(sourceTable, sourceColumn));

-        }

-        if (sourceColumn.isAutoIncrement() != targetColumn.isAutoIncrement())

-        {

-            changes.add(new ColumnAutoIncrementChange(sourceTable, sourceColumn));

-        }

-

-        return changes;

     }

 

     /**

@@ -403,7 +871,7 @@
      * @param fk    The original foreign key

      * @return The corresponding foreign key if found

      */

-    private ForeignKey findCorrespondingForeignKey(Table table, ForeignKey fk)

+    protected ForeignKey findCorrespondingForeignKey(Table table, ForeignKey fk)

     {

         for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++)

         {

@@ -428,7 +896,7 @@
      * @param index The original index

      * @return The corresponding index if found

      */

-    private Index findCorrespondingIndex(Table table, Index index)

+    protected Index findCorrespondingIndex(Table table, Index index)

     {

         for (int indexIdx = 0; indexIdx < table.getIndexCount(); indexIdx++)

         {

diff --git a/src/java/org/apache/ddlutils/alteration/Pair.java b/src/java/org/apache/ddlutils/alteration/Pair.java
new file mode 100644
index 0000000..89f5cce
--- /dev/null
+++ b/src/java/org/apache/ddlutils/alteration/Pair.java
@@ -0,0 +1,65 @@
+package org.apache.ddlutils.alteration;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Represents a pair of objects.
+ * 
+ * @version $Revision: $
+ */
+public class Pair
+{
+    /** The first object. */
+    private final Object _firstObj;
+    /** The first object. */
+    private final Object _secondObj;
+
+    /**
+     * Creates a pair object.
+     * 
+     * @param firstObj  The first object
+     * @param secondObj The second object
+     */
+    public Pair(Object firstObj, Object secondObj)
+    {
+        _firstObj  = firstObj;
+        _secondObj = secondObj;
+    }
+
+    /**
+     * Returns the first object of the pair.
+     * 
+     * @return The first object
+     */
+    public Object getFirst()
+    {
+        return _firstObj;
+    }
+
+    /**
+     * Returns the second object of the pair.
+     * 
+     * @return The second object
+     */
+    public Object getSecond()
+    {
+        return _secondObj;
+    }
+}
diff --git a/src/java/org/apache/ddlutils/alteration/PrimaryKeyChange.java b/src/java/org/apache/ddlutils/alteration/PrimaryKeyChange.java
index 267af8c..c371655 100644
--- a/src/java/org/apache/ddlutils/alteration/PrimaryKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/PrimaryKeyChange.java
@@ -30,40 +30,27 @@
  */

 public class PrimaryKeyChange extends TableChangeImplBase

 {

-    /** The columns making up the original primary key. */

-    private Column[] _oldPrimaryKeyColumns;

-    /** The columns making up the new primary key. */

-    private Column[] _newPrimaryKeyColumns;

+    /** The names of the columns making up the new primary key. */

+    private String[] _newPrimaryKeyColumns;

 

     /**

      * Creates a new change object.

      * 

-     * @param table                The table whose primary key is to be changed

-     * @param newPrimaryKeyColumns The columns making up the new primary key

+     * @param tableName            The name of the table whose primary key is to be changed

+     * @param newPrimaryKeyColumns The names of the columns making up the new primary key

      */

-    public PrimaryKeyChange(Table table, Column[] newPrimaryKeyColumns)

+    public PrimaryKeyChange(String tableName, String[] newPrimaryKeyColumns)

     {

-        super(table);

-        _oldPrimaryKeyColumns = table.getPrimaryKeyColumns();

+        super(tableName);

         _newPrimaryKeyColumns = newPrimaryKeyColumns;

     }

 

     /**

-     * Returns the columns making up the original primary key.

+     * Returns the names of the columns making up the new primary key.

      *

-     * @return The columns

+     * @return The column names

      */

-    public Column[] getOldPrimaryKeyColumns()

-    {

-        return _oldPrimaryKeyColumns;

-    }

-

-    /**

-     * Returns the columns making up the new primary key.

-     *

-     * @return The columns

-     */

-    public Column[] getNewPrimaryKeyColumns()

+    public String[] getNewPrimaryKeyColumns()

     {

         return _newPrimaryKeyColumns;

     }

@@ -73,17 +60,16 @@
      */

     public void apply(Database model, boolean caseSensitive)

     {

-        Table table = findChangedTable(model, caseSensitive);

+        Table    table  = findChangedTable(model, caseSensitive);

+        Column[] pkCols = table.getPrimaryKeyColumns();

 

-        for (int idx = 0; idx < _oldPrimaryKeyColumns.length; idx++)

+        for (int idx = 0; idx < pkCols.length; idx++)

         {

-            Column column = table.findColumn(_oldPrimaryKeyColumns[idx].getName(), caseSensitive);

-

-            column.setPrimaryKey(false);

+            pkCols[idx].setPrimaryKey(false);

         }

         for (int idx = 0; idx < _newPrimaryKeyColumns.length; idx++)

         {

-            Column column = table.findColumn(_newPrimaryKeyColumns[idx].getName(), caseSensitive);

+            Column column = table.findColumn(_newPrimaryKeyColumns[idx], caseSensitive);

 

             column.setPrimaryKey(true);

         }

diff --git a/src/java/org/apache/ddlutils/alteration/RecreateTableChange.java b/src/java/org/apache/ddlutils/alteration/RecreateTableChange.java
new file mode 100644
index 0000000..d9ce73e
--- /dev/null
+++ b/src/java/org/apache/ddlutils/alteration/RecreateTableChange.java
@@ -0,0 +1,103 @@
+package org.apache.ddlutils.alteration;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.ddlutils.model.CloneHelper;
+import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.Table;
+
+/**
+ * Represents the recreation of a table, i.e. creating a temporary table using the target table definition,
+ * copying the data from the original table into this temporary table, dropping the original table and
+ * finally renaming the temporary table to the final table. This change is only created by the model
+ * comparator if the table definition change predicate flags a table change as unsupported.
+ *  
+ * @version $Revision: $
+ */
+public class RecreateTableChange extends TableChangeImplBase
+{
+    /** The target table definition. */
+    private Table _targetTable;
+    /** The original table changes, one of which is unsupported by the current platform. */
+    private List  _originalChanges;
+
+    /**
+     * Creates a new change object for recreating a table. This change is used to specify that a table needs
+     * to be dropped and then re-created (with changes). In the standard model comparison algorithm, it will
+     * replace all direct changes to the table's columns (i.e. foreign key and index changes are unaffected).  
+     * 
+     * @param tableName       The name of the table
+     * @param targetTable     The table as it should be; note that the change object will keep a reference
+     *                        to this table which means that it should not be changed after creating this
+     *                        change object
+     * @param originalChanges The original changes that this change object replaces
+     */
+    public RecreateTableChange(String tableName, Table targetTable, List originalChanges)
+    {
+        super(tableName);
+        _targetTable     = targetTable;
+        _originalChanges = originalChanges;
+    }
+
+    /**
+     * Returns the original table changes, one of which is unsupported by the current platform.
+     * 
+     * @return The table changes
+     */
+    public List getOriginalChanges()
+    {
+        return _originalChanges;
+    }
+
+    /**
+     * Returns the target table definition. Due to when an object of this kind is created in the comparison
+     * process, this table object will not have any foreign keys pointing to or from it, i.e. it is
+     * independent of any model.
+     * 
+     * @return The table definition
+     */
+    public Table getTargetTable()
+    {
+        return _targetTable;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void apply(Database database, boolean caseSensitive)
+    {
+        // we only need to replace the table in the model, as there can't be a
+        // foreign key from or to it when these kind of changes are created
+        for (int tableIdx = 0; tableIdx < database.getTableCount(); tableIdx++)
+        {
+            Table curTable = database.getTable(tableIdx);
+
+            if ((caseSensitive  && curTable.getName().equals(getChangedTable())) ||
+                (!caseSensitive && curTable.getName().equalsIgnoreCase(getChangedTable())))
+            {
+                database.removeTable(tableIdx);
+                database.addTable(tableIdx, new CloneHelper().clone(_targetTable, true, false, database, caseSensitive));
+                break;
+            }
+        }
+    }
+}
diff --git a/src/java/org/apache/ddlutils/alteration/RemoveColumnChange.java b/src/java/org/apache/ddlutils/alteration/RemoveColumnChange.java
index a8d9f97..daf336e 100644
--- a/src/java/org/apache/ddlutils/alteration/RemoveColumnChange.java
+++ b/src/java/org/apache/ddlutils/alteration/RemoveColumnChange.java
@@ -19,9 +19,7 @@
  * under the License.

  */

 

-import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

 

 /**

  * Represents the removal of a column from a table.

@@ -33,12 +31,12 @@
     /**

      * Creates a new change object.

      * 

-     * @param table  The table to remove the column from

-     * @param column The column

+     * @param tableName  The name of the table to remove the column from

+     * @param columnName The column's name

      */

-    public RemoveColumnChange(Table table, Column column)

+    public RemoveColumnChange(String tableName, String columnName)

     {

-        super(table, column);

+        super(tableName, columnName);

     }

 

     /**

diff --git a/src/java/org/apache/ddlutils/alteration/RemoveForeignKeyChange.java b/src/java/org/apache/ddlutils/alteration/RemoveForeignKeyChange.java
index 601b0d5..5fb2f7f 100644
--- a/src/java/org/apache/ddlutils/alteration/RemoveForeignKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/RemoveForeignKeyChange.java
@@ -21,7 +21,6 @@
 

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.ForeignKey;

-import org.apache.ddlutils.model.Table;

 

 /**

  * Represents the removal of a foreign key from a table. Note that for

@@ -35,12 +34,12 @@
     /**

      * Creates a new change object.

      * 

-     * @param table      The table to remove the foreign key from

+     * @param tableName  The name of the table to remove the foreign key from

      * @param foreignKey The foreign key

      */

-    public RemoveForeignKeyChange(Table table, ForeignKey foreignKey)

+    public RemoveForeignKeyChange(String tableName, ForeignKey foreignKey)

     {

-        super(table, foreignKey);

+        super(tableName, foreignKey);

     }

 

     /**

diff --git a/src/java/org/apache/ddlutils/alteration/RemoveIndexChange.java b/src/java/org/apache/ddlutils/alteration/RemoveIndexChange.java
index 3887c28..e63bbd6 100644
--- a/src/java/org/apache/ddlutils/alteration/RemoveIndexChange.java
+++ b/src/java/org/apache/ddlutils/alteration/RemoveIndexChange.java
@@ -21,7 +21,6 @@
 

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Index;

-import org.apache.ddlutils.model.Table;

 

 /**

  * Represents the removal of an index from a table.

@@ -33,12 +32,12 @@
     /**

      * Creates a new change object.

      * 

-     * @param table The table to remove the index from

-     * @param index The index

+     * @param tableName The name of the table to remove the index from

+     * @param index     The index

      */

-    public RemoveIndexChange(Table table, Index index)

+    public RemoveIndexChange(String tableName, Index index)

     {

-        super(table, index);

+        super(tableName, index);

     }

 

     /**

diff --git a/src/java/org/apache/ddlutils/alteration/RemovePrimaryKeyChange.java b/src/java/org/apache/ddlutils/alteration/RemovePrimaryKeyChange.java
index ab1437d..9596752 100644
--- a/src/java/org/apache/ddlutils/alteration/RemovePrimaryKeyChange.java
+++ b/src/java/org/apache/ddlutils/alteration/RemovePrimaryKeyChange.java
@@ -30,29 +30,14 @@
  */

 public class RemovePrimaryKeyChange extends TableChangeImplBase

 {

-    /** The columns making up the primary key. */

-    private Column[] _primaryKeyColumns;

-

     /**

      * Creates a new change object.

      * 

-     * @param table             The table to remove the primary key from

-     * @param primaryKeyColumns The columns making up the primary key

+     * @param tableName The name of he table to remove the primary key from

      */

-    public RemovePrimaryKeyChange(Table table, Column[] primaryKeyColumns)

+    public RemovePrimaryKeyChange(String tableName)

     {

-        super(table);

-        _primaryKeyColumns = primaryKeyColumns;

-    }

-

-    /**

-     * Returns the primary key columns making up the primary key.

-     *

-     * @return The primary key columns

-     */

-    public Column[] getPrimaryKeyColumns()

-    {

-        return _primaryKeyColumns;

+        super(tableName);

     }

 

     /**

@@ -60,13 +45,12 @@
      */

     public void apply(Database model, boolean caseSensitive)

     {

-        Table table = findChangedTable(model, caseSensitive);

+        Table    table  = findChangedTable(model, caseSensitive);

+        Column[] pkCols = table.getPrimaryKeyColumns();

 

-        for (int idx = 0; idx < _primaryKeyColumns.length; idx++)

+        for (int idx = 0; idx < pkCols.length; idx++)

         {

-            Column column = table.findColumn(_primaryKeyColumns[idx].getName(), caseSensitive);

-

-            column.setPrimaryKey(false);

+            pkCols[idx].setPrimaryKey(false);

         }

     }

 }

diff --git a/src/java/org/apache/ddlutils/alteration/RemoveTableChange.java b/src/java/org/apache/ddlutils/alteration/RemoveTableChange.java
index cd712ea..7cb5dd5 100644
--- a/src/java/org/apache/ddlutils/alteration/RemoveTableChange.java
+++ b/src/java/org/apache/ddlutils/alteration/RemoveTableChange.java
@@ -25,6 +25,7 @@
 /**

  * Represents the removal of a table from a model.

  * 

+ * TODO: this should be a model change

  * @version $Revision: $

  */

 public class RemoveTableChange extends TableChangeImplBase

@@ -32,11 +33,11 @@
     /**

      * Creates a new change object.

      * 

-     * @param table The table

+     * @param tableName The name of the table to be removed

      */

-    public RemoveTableChange(Table table)

+    public RemoveTableChange(String tableName)

     {

-        super(table);

+        super(tableName);

     }

 

     /**

diff --git a/src/java/org/apache/ddlutils/alteration/TableChange.java b/src/java/org/apache/ddlutils/alteration/TableChange.java
index 45abe35..c3169d2 100644
--- a/src/java/org/apache/ddlutils/alteration/TableChange.java
+++ b/src/java/org/apache/ddlutils/alteration/TableChange.java
@@ -30,11 +30,11 @@
 public interface TableChange extends ModelChange

 {

     /**

-     * Returns the affected table from the original model.

+     * Returns the name of the affected table from the original model.

      * 

-     * @return The affected table

+     * @return The name of the affected table

      */

-    public Table getChangedTable();

+    public String getChangedTable();

 

     /**

      * Finds the table object corresponding to the changed table in the given database model.

diff --git a/src/java/org/apache/ddlutils/alteration/TableChangeImplBase.java b/src/java/org/apache/ddlutils/alteration/TableChangeImplBase.java
index 0cfed11..afede59 100644
--- a/src/java/org/apache/ddlutils/alteration/TableChangeImplBase.java
+++ b/src/java/org/apache/ddlutils/alteration/TableChangeImplBase.java
@@ -29,25 +29,25 @@
  */

 public abstract class TableChangeImplBase implements TableChange

 {

-    /** The affected table. */

-    private Table _table;

+    /** The name of the affected table. */

+    private String _tableName;

 

     /**

      * Creates a new change object.

      * 

-     * @param table The table

+     * @param tableName The table's name

      */

-    public TableChangeImplBase(Table table)

+    public TableChangeImplBase(String tableName)

     {

-        _table = table;

+        _tableName = tableName;

     }

 

     /**

      * {@inheritDoc}

      */

-    public Table getChangedTable()

+    public String getChangedTable()

     {

-        return _table;

+        return _tableName;

     }

 

     /**

@@ -55,6 +55,6 @@
      */

     public Table findChangedTable(Database model, boolean caseSensitive)

     {

-    	return model.findTable(_table.getName(), caseSensitive);

+    	return model.findTable(_tableName, caseSensitive);

     }

 }

diff --git a/src/java/org/apache/ddlutils/alteration/TableDefinitionChangesPredicate.java b/src/java/org/apache/ddlutils/alteration/TableDefinitionChangesPredicate.java
new file mode 100644
index 0000000..4ed5a6c
--- /dev/null
+++ b/src/java/org/apache/ddlutils/alteration/TableDefinitionChangesPredicate.java
@@ -0,0 +1,45 @@
+package org.apache.ddlutils.alteration;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.List;
+
+import org.apache.ddlutils.model.Table;
+
+/**
+ * Defines a predicate that allows platforms to state whether all of the given table definition
+ * changes (i.e. column changes and primary key changes) are supported by the platform or not.
+ * 
+ * @version $Revision: $
+ */
+public interface TableDefinitionChangesPredicate
+{
+    /**
+     * Evaluates the given list of table changes and determines whether they are supported.
+     * 
+     * @param intermediateTable The current table object which has certain non-table-definition
+     *                          changes already applied (those that would come before the give
+     *                          list of changes in the result of
+     *                          {@link ModelComparator#compare(org.apache.ddlutils.model.Database, org.apache.ddlutils.model.Database)}
+     * @param changes The non-empty list of changes
+     * @return <code>true</code> if the current plaform supports them
+     */
+    public boolean areSupported(Table intermediateTable, List changes);
+}
diff --git a/src/java/org/apache/ddlutils/model/CascadeActionEnum.java b/src/java/org/apache/ddlutils/model/CascadeActionEnum.java
index c129f18..d26ac00 100644
--- a/src/java/org/apache/ddlutils/model/CascadeActionEnum.java
+++ b/src/java/org/apache/ddlutils/model/CascadeActionEnum.java
@@ -26,8 +26,8 @@
 import org.apache.commons.lang.enums.ValuedEnum;
 
 /**
- * Represents the different cascade actions for {@link ForeignKey#onDelete} and
- * {@link ForeignKey#onUdate}.
+ * Represents the different cascade actions for the <code>onDelete</code> and
+ * <code>onUpdate</code> properties of {@link ForeignKey}.
  * 
  * @version $Revision: $
  */
diff --git a/src/java/org/apache/ddlutils/model/CloneHelper.java b/src/java/org/apache/ddlutils/model/CloneHelper.java
new file mode 100644
index 0000000..08e9294
--- /dev/null
+++ b/src/java/org/apache/ddlutils/model/CloneHelper.java
@@ -0,0 +1,221 @@
+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.
+ */
+
+/**
+ * Helper class that provides cloning of model elements.
+ * 
+ * @version $Revision: $
+ */
+public class CloneHelper
+{
+    /**
+     * Returns a deep clone of the given model object, including all tables, foreign keys, indexes etc.
+     *
+     * @param source The source model
+     * @return The clone
+     */
+    public Database clone(Database source)
+    {
+        Database result = new Database();
+
+        result.setName(source.getName());
+        result.setIdMethod(source.getIdMethod());
+        result.setVersion(source.getVersion());
+
+        for (int tableIdx = 0; tableIdx < source.getTableCount(); tableIdx++)
+        {
+            Table sourceTable = source.getTable(tableIdx);
+
+            result.addTable(clone(sourceTable, true, false, result, true));
+        }
+        for (int tableIdx = 0; tableIdx < source.getTableCount(); tableIdx++)
+        {
+            Table sourceTable = source.getTable(tableIdx);
+            Table clonedTable = result.getTable(tableIdx);
+
+            for (int fkIdx = 0; fkIdx < sourceTable.getForeignKeyCount(); fkIdx++)
+            {
+                ForeignKey sourceFk = sourceTable.getForeignKey(fkIdx);
+
+                clonedTable.addForeignKey(clone(sourceFk, clonedTable, result, true));
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given table.
+     * 
+     * @param source           The source table
+     * @param cloneIndexes     Whether to clone indexes; if <code>false</code> then the clone will
+     *                         not have any indexes
+     * @param cloneForeignKeys Whether to clone foreign kets; if <code>false</code> then the clone
+     *                         will not have any foreign keys
+     * @param targetModel      The target model, can be <code>null</code> if
+     *                         <code>cloneForeignKeys=false</code>
+     * @param caseSensitive    Whether comparison is case sensitive (for cloning foreign keys)
+     * @return The clone
+     */
+    public Table clone(Table source, boolean cloneIndexes, boolean cloneForeignKeys, Database targetModel, boolean caseSensitive)
+    {
+        Table result = new Table();
+
+        result.setCatalog(source.getCatalog());
+        result.setSchema(source.getSchema());
+        result.setName(source.getName());
+        result.setType(source.getType());
+
+        for (int colIdx = 0; colIdx < source.getColumnCount(); colIdx++)
+        {
+            result.addColumn(clone(source.getColumn(colIdx), true));
+        }
+        if (cloneIndexes)
+        {
+            for (int indexIdx = 0; indexIdx < source.getIndexCount(); indexIdx++)
+            {
+                result.addIndex(clone(source.getIndex(indexIdx), result, true));
+            }
+        }
+        if (cloneForeignKeys)
+        {
+            for (int fkIdx = 0; fkIdx < source.getForeignKeyCount(); fkIdx++)
+            {
+                result.addForeignKey(clone(source.getForeignKey(fkIdx), result, targetModel, caseSensitive));
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given source column.
+     * 
+     * @param source                The source column
+     * @param clonePrimaryKeyStatus Whether to clone the column's primary key status; if <code>false</code>
+     *                              then the clone will not be a primary key column
+     * @return The clone
+     */
+    public Column clone(Column source, boolean clonePrimaryKeyStatus)
+    {
+        Column result = new Column();
+
+        result.setName(source.getName());
+        result.setJavaName(source.getJavaName());
+        result.setPrimaryKey(clonePrimaryKeyStatus ? source.isPrimaryKey() : false);
+        result.setRequired(source.isRequired());
+        result.setAutoIncrement(source.isAutoIncrement());
+        result.setTypeCode(source.getTypeCode());
+        result.setSize(source.getSize());
+        result.setDefaultValue(source.getDefaultValue());
+
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given source index.
+     * 
+     * @param source        The source index
+     * @param targetTable   The table whose columns shall be used by the clone
+     * @param caseSensitive Whether comparison is case sensitive (for finding the columns
+     *                      in the target table)
+     * @return The clone
+     */
+    public Index clone(Index source, Table targetTable, boolean caseSensitive)
+    {
+        Index result = (source.isUnique() ? (Index)new UniqueIndex() : (Index)new NonUniqueIndex());
+
+        result.setName(source.getName());
+        for (int colIdx = 0; colIdx < source.getColumnCount(); colIdx++)
+        {
+            IndexColumn column = source.getColumn(colIdx);
+
+            result.addColumn(clone(column, targetTable, caseSensitive));
+        }
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given index column.
+     * 
+     * @param source        The source index column
+     * @param targetTable   The table containing the column to be used by the clone
+     * @param caseSensitive Whether comparison is case sensitive (for finding the columns
+     *                      in the target table)
+     * @return The clone
+     */
+    public IndexColumn clone(IndexColumn source, Table targetTable, boolean caseSensitive)
+    {
+        IndexColumn result = new IndexColumn();
+
+        result.setColumn(targetTable.findColumn(source.getName(), caseSensitive));
+        result.setOrdinalPosition(source.getOrdinalPosition());
+        result.setSize(source.getSize());
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given source foreign key.
+     * 
+     * @param source        The source foreign key
+     * @param owningTable   The table owning the source foreign key
+     * @param targetModel   The target model containing the tables that the clone shall link
+     * @param caseSensitive Whether comparison is case sensitive (for finding the columns
+     *                      in the target model)
+     * @return The clone
+     */
+    public ForeignKey clone(ForeignKey source, Table owningTable, Database targetModel, boolean caseSensitive)
+    {
+        ForeignKey result       = new ForeignKey();
+        Table      foreignTable = targetModel.findTable(source.getForeignTableName(), caseSensitive);
+        
+        result.setName(source.getName());
+        result.setForeignTable(foreignTable);
+        result.setAutoIndexPresent(source.isAutoIndexPresent());
+
+        for (int refIdx = 0; refIdx < source.getReferenceCount(); refIdx++)
+        {
+            Reference ref = source.getReference(refIdx);
+
+            result.addReference(clone(ref, owningTable, foreignTable, caseSensitive));
+        }
+
+        return result;
+    }
+
+    /**
+     * Returns a clone of the given source reference.
+     * 
+     * @param source        The source reference
+     * @param localTable    The table containing the local column to be used by the reference
+     * @param foreignTable  The table containing the foreign column to be used by the reference
+     * @param caseSensitive Whether comparison is case sensitive (for finding the columns
+     *                      in the tables)
+     * @return The clone
+     */
+    public Reference clone(Reference source, Table localTable, Table foreignTable, boolean caseSensitive)
+    {
+        Reference result = new Reference();
+
+        result.setLocalColumn(localTable.findColumn(source.getLocalColumnName(), caseSensitive));
+        result.setForeignColumn(foreignTable.findColumn(source.getForeignColumnName(), caseSensitive));
+        return result;
+    }
+}
diff --git a/src/java/org/apache/ddlutils/model/Column.java b/src/java/org/apache/ddlutils/model/Column.java
index 46e2300..fb7cd8c 100644
--- a/src/java/org/apache/ddlutils/model/Column.java
+++ b/src/java/org/apache/ddlutils/model/Column.java
@@ -36,7 +36,7 @@
  * 
  * @version $Revision$
  */
-public class Column implements Cloneable, Serializable
+public class Column implements Serializable
 {
     /** Unique ID for serialization purposes. */
     private static final long serialVersionUID = -6226348998874210093L;
@@ -483,29 +483,6 @@
     /**
      * {@inheritDoc}
      */
-    public Object clone() throws CloneNotSupportedException
-    {
-        Column result = (Column)super.clone();
-
-        result._name            = _name;
-        result._javaName        = _javaName;
-        result._primaryKey      = _primaryKey;
-        result._required        = _required;
-        result._autoIncrement   = _autoIncrement;
-        result._typeCode        = _typeCode;
-        result._type            = _type;
-        result._size            = _size;
-        result._defaultValue    = _defaultValue;
-        result._scale           = _scale;
-        result._size            = _size;
-        result._sizeAsInt       = _sizeAsInt;
-
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public boolean equals(Object obj)
     {
         if (obj instanceof Column)
diff --git a/src/java/org/apache/ddlutils/model/Database.java b/src/java/org/apache/ddlutils/model/Database.java
index cf47357..9cef551 100644
--- a/src/java/org/apache/ddlutils/model/Database.java
+++ b/src/java/org/apache/ddlutils/model/Database.java
@@ -40,7 +40,7 @@
  *
  * @version $Revision$
  */
-public class Database implements Serializable, Cloneable
+public class Database implements Serializable
 {
     /** Unique ID for serialization purposes. */
     private static final long serialVersionUID = -3160443396757573868L;
@@ -64,22 +64,32 @@
      */
     public void mergeWith(Database otherDb) throws ModelException
     {
-        for (Iterator it = otherDb._tables.iterator(); it.hasNext();)
+        CloneHelper cloneHelper = new CloneHelper();
+
+        for (int tableIdx = 0; tableIdx < otherDb.getTableCount(); tableIdx++)
         {
-            Table table = (Table)it.next();
+            Table table = otherDb.getTable(tableIdx);
 
             if (findTable(table.getName()) != null)
             {
                 // TODO: It might make more sense to log a warning and overwrite the table (or merge them) ?
                 throw new ModelException("Cannot merge the models because table "+table.getName()+" already defined in this model");
             }
-            try
+            else
             {
-                addTable((Table)table.clone());
+                addTable(cloneHelper.clone(table, true, false, this, true));
             }
-            catch (CloneNotSupportedException ex)
+        }
+        for (int tableIdx = 0; tableIdx < otherDb.getTableCount(); tableIdx++)
+        {
+            Table otherTable = otherDb.getTable(tableIdx);
+            Table localTable = findTable(otherTable.getName());
+
+            for (int fkIdx = 0; fkIdx < otherTable.getForeignKeyCount(); fkIdx++)
             {
-                // won't happen
+                ForeignKey fk = otherTable.getForeignKey(fkIdx);
+
+                localTable.addForeignKey(cloneHelper.clone(fk, localTable, this, false));
             }
         }
     }
@@ -292,7 +302,7 @@
                 }
                 if (namesOfProcessedColumns.contains(column.getName()))
                 {
-                    throw new ModelException("There are multiple column with the name "+column.getName()+" in the table "+curTable.getName());
+                    throw new ModelException("There are multiple columns with the name "+column.getName()+" in the table "+curTable.getName());
                 }
                 namesOfProcessedColumns.add(column.getName());
 
@@ -531,21 +541,6 @@
     /**
      * {@inheritDoc}
      */
-    public Object clone() throws CloneNotSupportedException
-    {
-        Database result = (Database)super.clone();
-
-        result._name     = _name;
-        result._idMethod = _idMethod;
-        result._version  = _version;
-        result._tables   = (ArrayList)_tables.clone();
-
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public boolean equals(Object obj)
     {
         if (obj instanceof Database)
diff --git a/src/java/org/apache/ddlutils/model/ForeignKey.java b/src/java/org/apache/ddlutils/model/ForeignKey.java
index 3d3fc82..8fede8d 100644
--- a/src/java/org/apache/ddlutils/model/ForeignKey.java
+++ b/src/java/org/apache/ddlutils/model/ForeignKey.java
@@ -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,7 +371,8 @@
     }
 
     /**
-     * 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
      */
@@ -334,25 +384,6 @@
     /**
      * {@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;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public boolean equals(Object obj)
     {
         if (obj instanceof ForeignKey)
diff --git a/src/java/org/apache/ddlutils/model/Index.java b/src/java/org/apache/ddlutils/model/Index.java
index d14af60..accd562 100644
--- a/src/java/org/apache/ddlutils/model/Index.java
+++ b/src/java/org/apache/ddlutils/model/Index.java
@@ -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
@@ -109,6 +118,14 @@
     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.
      * 
      * @param otherIndex The other index
diff --git a/src/java/org/apache/ddlutils/model/IndexColumn.java b/src/java/org/apache/ddlutils/model/IndexColumn.java
index b88a259..101f183 100644
--- a/src/java/org/apache/ddlutils/model/IndexColumn.java
+++ b/src/java/org/apache/ddlutils/model/IndexColumn.java
@@ -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}
      */
diff --git a/src/java/org/apache/ddlutils/model/IndexImpBase.java b/src/java/org/apache/ddlutils/model/IndexImplBase.java
similarity index 84%
rename from src/java/org/apache/ddlutils/model/IndexImpBase.java
rename to src/java/org/apache/ddlutils/model/IndexImplBase.java
index e01b033..81cde21 100644
--- a/src/java/org/apache/ddlutils/model/IndexImpBase.java
+++ b/src/java/org/apache/ddlutils/model/IndexImplBase.java
@@ -21,12 +21,14 @@
 

 import java.util.ArrayList;

 

+import org.apache.ddlutils.util.StringUtils;

+

 /**

- * Base class for indices.

+ * Base class for indexes.

  * 

  * @version $Revision: $

  */

-public abstract class IndexImpBase implements Index

+public abstract class IndexImplBase implements Index

 {

     /** The name of the index. */

     protected String    _name;

@@ -93,6 +95,23 @@
     /**

      * {@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)

diff --git a/src/java/org/apache/ddlutils/model/NonUniqueIndex.java b/src/java/org/apache/ddlutils/model/NonUniqueIndex.java
index 6ad9f9e..9fa2edd 100644
--- a/src/java/org/apache/ddlutils/model/NonUniqueIndex.java
+++ b/src/java/org/apache/ddlutils/model/NonUniqueIndex.java
@@ -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;

@@ -47,6 +47,14 @@
      */

     public Object clone() throws CloneNotSupportedException

     {

+        return getClone();

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public Index getClone() throws ModelException

+    {

         NonUniqueIndex result = new NonUniqueIndex();

 

         result._name    = _name;

diff --git a/src/java/org/apache/ddlutils/model/Reference.java b/src/java/org/apache/ddlutils/model/Reference.java
index d710d83..d620b76 100644
--- a/src/java/org/apache/ddlutils/model/Reference.java
+++ b/src/java/org/apache/ddlutils/model/Reference.java
@@ -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;
@@ -180,19 +180,6 @@
     /**
      * {@inheritDoc}
      */
-    public Object clone() throws CloneNotSupportedException
-    {
-        Reference result = (Reference)super.clone();
-
-        result._localColumnName   = _localColumnName;
-        result._foreignColumnName = _foreignColumnName;
-
-        return result;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
     public boolean equals(Object obj)
     {
         if (obj instanceof Reference)
diff --git a/src/java/org/apache/ddlutils/model/Table.java b/src/java/org/apache/ddlutils/model/Table.java
index 03b9d53..f589a7d 100644
--- a/src/java/org/apache/ddlutils/model/Table.java
+++ b/src/java/org/apache/ddlutils/model/Table.java
@@ -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.
      * 
@@ -755,24 +791,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}
-     */
     public boolean equals(Object obj)
     {
         if (obj instanceof Table)
diff --git a/src/java/org/apache/ddlutils/model/UniqueIndex.java b/src/java/org/apache/ddlutils/model/UniqueIndex.java
index ae97474..0130bee 100644
--- a/src/java/org/apache/ddlutils/model/UniqueIndex.java
+++ b/src/java/org/apache/ddlutils/model/UniqueIndex.java
@@ -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;
@@ -48,6 +48,14 @@
      */
     public Object clone() throws CloneNotSupportedException
     {
+        return getClone();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Index getClone() throws ModelException
+    {
         UniqueIndex result = new UniqueIndex();
 
         result._name    = _name;
diff --git a/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java b/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java
new file mode 100644
index 0000000..1e56fa5
--- /dev/null
+++ b/src/java/org/apache/ddlutils/platform/DefaultTableDefinitionChangesPredicate.java
@@ -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
diff --git a/src/java/org/apache/ddlutils/platform/JdbcModelReader.java b/src/java/org/apache/ddlutils/platform/JdbcModelReader.java
index 599a2bc..6372e51 100644
--- a/src/java/org/apache/ddlutils/platform/JdbcModelReader.java
+++ b/src/java/org/apache/ddlutils/platform/JdbcModelReader.java
@@ -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 ");
diff --git a/src/java/org/apache/ddlutils/platform/PlatformImplBase.java b/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
index fd93fb5..312bce8 100644
--- a/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
+++ b/src/java/org/apache/ddlutils/platform/PlatformImplBase.java
@@ -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,11 +429,59 @@
      */

     public void createTables(Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

     {

+        createModel(model, dropTablesFirst, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void createTables(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    {

+        createModel(model, params, dropTablesFirst, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void createTables(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    {

+        createModel(connection, model, dropTablesFirst, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    {

+        createModel(connection, model, params, dropTablesFirst, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError)

+    {

+        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

         {

-            createTables(connection, model, dropTablesFirst, continueOnError);

+            createModel(connection, model, dropTablesFirst, continueOnError);

         }

         finally

         {

@@ -417,9 +492,9 @@
     /**

      * {@inheritDoc}

      */

-    public void createTables(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    public void createModel(Connection connection, Database model, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

     {

-        String sql = getCreateTablesSql(model, dropTablesFirst, continueOnError);

+        String sql = getCreateModelSql(model, dropTablesFirst, continueOnError);

 

         evaluateBatch(connection, sql, continueOnError);

     }

@@ -427,7 +502,34 @@
     /**

      * {@inheritDoc}

      */

-    public String getCreateTablesSql(Database model, boolean dropTablesFirst, boolean continueOnError)

+    public void createModel(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    {

+        Connection connection = borrowConnection();

+

+        try

+        {

+            createModel(connection, model, params, dropTablesFirst, continueOnError);

+        }

+        finally

+        {

+            returnConnection(connection);

+        }

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void createModel(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

+    {

+        String sql = getCreateModelSql(model, params, dropTablesFirst, continueOnError);

+

+        evaluateBatch(connection, sql, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public String getCreateModelSql(Database model, boolean dropTablesFirst, boolean continueOnError)

     {

         String sql = null;

 

@@ -449,34 +551,7 @@
     /**

      * {@inheritDoc}

      */

-    public void createTables(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

-    {

-        Connection connection = borrowConnection();

-

-        try

-        {

-            createTables(connection, model, params, dropTablesFirst, continueOnError);

-        }

-        finally

-        {

-            returnConnection(connection);

-        }

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public void createTables(Connection connection, Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError) throws DatabaseOperationException

-    {

-        String sql = getCreateTablesSql(model, params, dropTablesFirst, continueOnError);

-

-        evaluateBatch(connection, sql, continueOnError);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public String getCreateTablesSql(Database model, CreationParameters params, boolean dropTablesFirst, boolean continueOnError)

+    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,32 +734,9 @@
      */

     public void alterTables(Connection connection, Database desiredModel, boolean continueOnError) throws DatabaseOperationException

     {

-        String sql = getAlterTablesSql(connection, desiredModel);

-

-        evaluateBatch(connection, sql, continueOnError);

-    }

-

-    /**

-     * {@inheritDoc}

-     */

-    public String getAlterTablesSql(Connection connection, Database desiredModel) 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, continueOnError);

     }

 

     /**

@@ -601,86 +744,89 @@
      */

     public void alterTables(Connection connection, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException

     {

-        String sql = getAlterTablesSql(connection, desiredModel, params);

+        Database currentModel = readModelFromDatabase(connection, desiredModel.getName());

 

-        evaluateBatch(connection, sql, continueOnError);

+        alterModel(currentModel, desiredModel, params, 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, boolean continueOnError) throws DatabaseOperationException

     {

-        String   sql          = null;

-        Database currentModel = readModelFromDatabase(connection, desiredModel.getName());

+        Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);

 

-        try

-        {

-            StringWriter buffer = new StringWriter();

-

-            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, continueOnError);

     }

 

-	/**

+    /**

      * {@inheritDoc}

      */

-	public void alterTables(String catalog, String schema, String[] tableTypes, Database desiredModel, boolean continueOnError) throws DatabaseOperationException

-	{

+    public void alterTables(Connection connection, String catalog, String schema, String[] tableTypes, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException

+    {

+        Database currentModel = readModelFromDatabase(connection, desiredModel.getName(), catalog, schema, tableTypes);

+

+        alterModel(currentModel, desiredModel, params, continueOnError);

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    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, String catalog, String schema, String[] tableTypes, Database desiredModel) throws DatabaseOperationException

-	{

-        String   sql          = null;

+    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

+    {

         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);

-

-        evaluateBatch(connection, sql, continueOnError);

-	}

-

-	/**

-     * {@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, 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, continueOnError);

         }

-        catch (IOException ex)

+        finally

         {

-            // won't happen because we're using a string writer

+            returnConnection(connection);

         }

-        return sql;

-	}

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void alterModel(Database currentModel, Database desiredModel, CreationParameters params, boolean continueOnError) throws DatabaseOperationException

+    {

+        Connection connection = borrowConnection();

+

+        try

+        {

+            alterModel(connection, currentModel, desiredModel, params, continueOnError);

+        }

+        finally

+        {

+            returnConnection(connection);

+        }

+    }

+

+    /**

+     * {@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)

diff --git a/src/java/org/apache/ddlutils/platform/SqlBuilder.java b/src/java/org/apache/ddlutils/platform/SqlBuilder.java
index ca4859e..09e93ae 100644
--- a/src/java/org/apache/ddlutils/platform/SqlBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/SqlBuilder.java
@@ -25,17 +25,10 @@
 import java.sql.Types;
 import java.text.DateFormat;
 import java.text.NumberFormat;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 
-import org.apache.commons.collections.Closure;
-import org.apache.commons.collections.CollectionUtils;
-import org.apache.commons.collections.Predicate;
 import org.apache.commons.collections.map.ListOrderedMap;
 import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
@@ -43,26 +36,6 @@
 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.ColumnAutoIncrementChange;
-import org.apache.ddlutils.alteration.ColumnDataTypeChange;
-import org.apache.ddlutils.alteration.ColumnDefaultValueChange;
-import org.apache.ddlutils.alteration.ColumnOrderChange;
-import org.apache.ddlutils.alteration.ColumnRequiredChange;
-import org.apache.ddlutils.alteration.ColumnSizeChange;
-import org.apache.ddlutils.alteration.ModelChange;
-import org.apache.ddlutils.alteration.ModelComparator;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-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.model.CascadeActionEnum;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
@@ -70,11 +43,8 @@
 import org.apache.ddlutils.model.Index;
 import org.apache.ddlutils.model.IndexColumn;
 import org.apache.ddlutils.model.ModelException;
-import org.apache.ddlutils.model.Reference;
 import org.apache.ddlutils.model.Table;
 import org.apache.ddlutils.model.TypeMap;
-import org.apache.ddlutils.util.CallbackClosure;
-import org.apache.ddlutils.util.MultiInstanceofPredicate;
 
 /**
  * This class is a collection of Strategy methods for creating the DDL required to create and drop 
@@ -446,611 +416,7 @@
         }
 
         // we're writing the external foreignkeys last to ensure that all referenced tables are already defined
-        createExternalForeignKeys(database);
-    }
-
-    /**
-     * Generates the DDL to modify an existing database so the schema matches
-     * the specified database schema by using drops, modifications and additions.
-     * Database-specific implementations can change aspect of this algorithm by
-     * redefining the individual methods that compromise it.
-     *
-     * @param currentModel  The current database schema
-     * @param desiredModel  The desired database schema
-     * @param params        The parameters used in the creation of new tables. Note that for existing
-     *                      tables, the parameters won't be applied
-     */
-    public void alterDatabase(Database currentModel, Database desiredModel, CreationParameters params) throws IOException
-    {
-        ModelComparator comparator = new ModelComparator(getPlatformInfo(),
-                                                         getPlatform().isDelimitedIdentifierModeOn());
-        List            changes    = comparator.compare(currentModel, desiredModel);
-
-        processChanges(currentModel, desiredModel, changes, params);
-    }
-
-    /**
-     * Calls the given closure for all changes that are of one of the given types, and
-     * then removes them from the changes collection.
-     * 
-     * @param changes     The changes
-     * @param changeTypes The types to search for
-     * @param closure     The closure to invoke
-     */
-    protected void applyForSelectedChanges(Collection changes, Class[] changeTypes, final Closure closure)
-    {
-        final Predicate predicate = new MultiInstanceofPredicate(changeTypes);
-
-        // basically we filter the changes for all objects where the above predicate
-        // returns true, and for these filtered objects we invoke the given closure
-        CollectionUtils.filter(changes,
-                               new Predicate()
-                               {
-                                   public boolean evaluate(Object obj)
-                                   {
-                                       if (predicate.evaluate(obj))
-                                       {
-                                           closure.execute(obj);
-                                           return false;
-                                       }
-                                       else
-                                       {
-                                           return true;
-                                       }
-                                   }
-                               });
-    }
-    
-    /**
-     * Processes the changes. The default argument performs several passes:
-     * <ol>
-     * <li>{@link org.apache.ddlutils.alteration.RemoveForeignKeyChange} and
-     *     {@link org.apache.ddlutils.alteration.RemoveIndexChange} come first
-     *     to allow for e.g. subsequent primary key changes or column removal.</li>
-     * <li>{@link org.apache.ddlutils.alteration.RemoveTableChange}
-     *     comes after the removal of foreign keys and indices.</li> 
-     * <li>These are all handled together:<br/>
-     *     {@link org.apache.ddlutils.alteration.RemovePrimaryKeyChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.AddPrimaryKeyChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.PrimaryKeyChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.RemoveColumnChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.AddColumnChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.ColumnAutoIncrementChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.ColumnDefaultValueChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.ColumnRequiredChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.ColumnDataTypeChange}<br/>
-     *     {@link org.apache.ddlutils.alteration.ColumnSizeChange}<br/>
-     *     The reason for this is that the default algorithm rebuilds the table for these
-     *     changes and thus their order is irrelevant.</li>
-     * <li>{@link org.apache.ddlutils.alteration.AddTableChange}<br/>
-     *     needs to come after the table removal (so that tables of the same name are
-     *     removed) and before the addition of foreign keys etc.</li>
-     * <li>{@link org.apache.ddlutils.alteration.AddForeignKeyChange} and
-     *     {@link org.apache.ddlutils.alteration.AddIndexChange} come last
-     *     after table/column/primary key additions or changes.</li>
-     * </ol>
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @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
-     */
-    protected void processChanges(Database           currentModel,
-                                  Database           desiredModel,
-                                  List               changes,
-                                  CreationParameters params) throws IOException
-    {
-        CallbackClosure callbackClosure = new CallbackClosure(this,
-                                                              "processChange",
-                                                              new Class[] { Database.class, Database.class, CreationParameters.class, null },
-                                                              new Object[] { currentModel, desiredModel, params, null });
-
-        // 1st pass: removing external constraints and indices
-        applyForSelectedChanges(changes,
-                                new Class[] { RemoveForeignKeyChange.class,
-                                              RemoveIndexChange.class },
-                                callbackClosure);
-
-        // 2nd pass: removing tables
-        applyForSelectedChanges(changes,
-                                new Class[] { RemoveTableChange.class },
-                                callbackClosure);
-
-        // 3rd pass: changing the structure of tables
-        Predicate predicate = new MultiInstanceofPredicate(new Class[] { RemovePrimaryKeyChange.class,
-                                                                         AddPrimaryKeyChange.class,
-                                                                         PrimaryKeyChange.class,
-                                                                         RemoveColumnChange.class,
-                                                                         AddColumnChange.class,
-                                                                         ColumnOrderChange.class,
-                                                                         ColumnAutoIncrementChange.class,
-                                                                         ColumnDefaultValueChange.class,
-                                                                         ColumnRequiredChange.class,
-                                                                         ColumnDataTypeChange.class,
-                                                                         ColumnSizeChange.class });
-
-        processTableStructureChanges(currentModel,
-                                     desiredModel,
-                                     params,
-                                     CollectionUtils.select(changes, predicate));
-
-        // 4th pass: adding tables
-        applyForSelectedChanges(changes,
-                                new Class[] { AddTableChange.class },
-                                callbackClosure);
-        // 5th pass: adding external constraints and indices
-        applyForSelectedChanges(changes,
-                                new Class[] { AddForeignKeyChange.class,
-                                              AddIndexChange.class },
-                                callbackClosure);
-    }
-
-    /**
-     * This is a fall-through callback which generates a warning because a specific
-     * change type wasn't handled.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 CreationParameters params,
-                                 ModelChange        change) throws IOException
-    {
-        _log.warn("Change of type " + change.getClass() + " was not handled");
-    }
-
-    /**
-     * Processes the change representing the removal of a foreign key.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 CreationParameters     params,
-                                 RemoveForeignKeyChange change) throws IOException
-    {
-        writeExternalForeignKeyDropStmt(change.getChangedTable(), change.getChangedForeignKey());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change representing the removal of an index.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 CreationParameters params,
-                                 RemoveIndexChange  change) throws IOException
-    {
-        writeExternalIndexDropStmt(change.getChangedTable(), change.getChangedIndex());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change representing the removal of a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 CreationParameters params,
-                                 RemoveTableChange  change) throws IOException
-    {
-        dropTable(change.getChangedTable());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change representing the addition of a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 CreationParameters params,
-                                 AddTableChange     change) throws IOException
-    {
-        createTable(desiredModel, change.getNewTable(), params == null ? null : params.getParametersFor(change.getNewTable()));
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change representing the addition of a foreign key.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database            currentModel,
-                                 Database            desiredModel,
-                                 CreationParameters  params,
-                                 AddForeignKeyChange change) throws IOException
-    {
-        writeExternalForeignKeyCreateStmt(desiredModel,
-                                          change.getChangedTable(),
-                                          change.getNewForeignKey());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change representing the addition of an index.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 CreationParameters params,
-                                 AddIndexChange     change) throws IOException
-    {
-        writeExternalIndexCreateStmt(change.getChangedTable(), change.getNewIndex());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the changes to the structure of tables.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired 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 changes      The change objects
-     */
-    protected void processTableStructureChanges(Database           currentModel,
-                                                Database           desiredModel,
-                                                CreationParameters params,
-                                                Collection         changes) throws IOException
-    {
-        ListOrderedMap changesPerTable = new ListOrderedMap();
-        ListOrderedMap unchangedFKs    = new ListOrderedMap();
-        boolean        caseSensitive   = getPlatform().isDelimitedIdentifierModeOn();
-
-        // we first sort the changes for the tables
-        // however since the changes might contain source or target tables
-        // we use the names rather than the table objects
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-            String      name   = change.getChangedTable().getName();
-
-            if (!caseSensitive)
-            {
-                name = name.toUpperCase();
-            }
-
-            List changesForTable = (ArrayList)changesPerTable.get(name);
-
-            if (changesForTable == null)
-            {
-                changesForTable = new ArrayList();
-                changesPerTable.put(name, changesForTable);
-                unchangedFKs.put(name, getUnchangedForeignKeys(currentModel, desiredModel, name));
-            }
-            changesForTable.add(change);
-        }
-        // we also need to drop the foreign keys of the unchanged tables referencing the changed tables
-        addRelevantFKsFromUnchangedTables(currentModel, desiredModel, changesPerTable.keySet(), unchangedFKs);
-
-        // we're dropping the unchanged foreign keys
-        for (Iterator tableFKIt = unchangedFKs.entrySet().iterator(); tableFKIt.hasNext();)
-        {
-            Map.Entry entry       = (Map.Entry)tableFKIt.next();
-            Table     targetTable = desiredModel.findTable((String)entry.getKey(), caseSensitive);
-
-            for (Iterator fkIt = ((List)entry.getValue()).iterator(); fkIt.hasNext();)
-            {
-                writeExternalForeignKeyDropStmt(targetTable, (ForeignKey)fkIt.next());
-            }
-        }
-
-        // We're using a copy of the current model so that the table structure changes can
-        // modify it
-        Database copyOfCurrentModel = null;
-
-        try
-        {
-            copyOfCurrentModel = (Database)currentModel.clone();
-        }
-        catch (CloneNotSupportedException ex)
-        {
-            throw new DdlUtilsException(ex);
-        }
-        
-        for (Iterator tableChangeIt = changesPerTable.entrySet().iterator(); tableChangeIt.hasNext();)
-        {
-            Map.Entry entry       = (Map.Entry)tableChangeIt.next();
-            Table     targetTable = desiredModel.findTable((String)entry.getKey(), caseSensitive);
-
-            processTableStructureChanges(copyOfCurrentModel,
-                                         desiredModel,
-                                         (String)entry.getKey(),
-                                         params == null ? null : params.getParametersFor(targetTable),
-                                         (List)entry.getValue());
-        }
-        // and finally we're re-creating the unchanged foreign keys
-        for (Iterator tableFKIt = unchangedFKs.entrySet().iterator(); tableFKIt.hasNext();)
-        {
-            Map.Entry entry       = (Map.Entry)tableFKIt.next();
-            Table     targetTable = desiredModel.findTable((String)entry.getKey(), caseSensitive);
-
-            for (Iterator fkIt = ((List)entry.getValue()).iterator(); fkIt.hasNext();)
-            {
-                writeExternalForeignKeyCreateStmt(desiredModel, targetTable, (ForeignKey)fkIt.next());
-            }
-        }
-    }
-
-    /**
-     * Determines the unchanged foreign keys of the indicated table.
-     * 
-     * @param currentModel The current model
-     * @param desiredModel The desired model
-     * @param tableName    The name of the table
-     * @return The list of unchanged foreign keys
-     */
-    private List getUnchangedForeignKeys(Database currentModel,
-                                         Database desiredModel,
-                                         String   tableName)
-    {
-        ArrayList unchangedFKs  = new ArrayList();
-        boolean   caseSensitive = getPlatform().isDelimitedIdentifierModeOn();
-        Table     sourceTable   = currentModel.findTable(tableName, caseSensitive);
-        Table     targetTable   = desiredModel.findTable(tableName, caseSensitive);
-
-        for (int idx = 0; idx < targetTable.getForeignKeyCount(); idx++)
-        {
-            ForeignKey targetFK = targetTable.getForeignKey(idx);
-            ForeignKey sourceFK = sourceTable.findForeignKey(targetFK, caseSensitive);
-
-            if (sourceFK != null)
-            {
-                unchangedFKs.add(targetFK);
-            }
-        }
-        return unchangedFKs;
-    }
-
-    /**
-     * Adds the foreign keys of the unchanged tables that reference changed tables
-     * to the given map.
-     * 
-     * @param currentModel              The current model
-     * @param desiredModel              The desired model
-     * @param namesOfKnownChangedTables The known names of changed tables
-     * @param fksPerTable               The map table name -> foreign keys to which
-     *                                  found foreign keys will be added to
-     */
-    private void addRelevantFKsFromUnchangedTables(Database currentModel,
-                                                   Database desiredModel,
-                                                   Set      namesOfKnownChangedTables,
-                                                   Map      fksPerTable)
-    {
-        boolean caseSensitive = getPlatform().isDelimitedIdentifierModeOn();
-
-        for (int tableIdx = 0; tableIdx < desiredModel.getTableCount(); tableIdx++)
-        {
-            Table  targetTable = desiredModel.getTable(tableIdx);
-            String name        = targetTable.getName();
-            Table  sourceTable = currentModel.findTable(name, caseSensitive);
-            List   relevantFks = null;
-
-            if (!caseSensitive)
-            {
-                name = name.toUpperCase();
-            }
-            if ((sourceTable != null) && !namesOfKnownChangedTables.contains(name))
-            {
-                for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)
-                {
-                    ForeignKey targetFk = targetTable.getForeignKey(fkIdx);
-                    ForeignKey sourceFk = sourceTable.findForeignKey(targetFk, caseSensitive);
-                    String     refName  = targetFk.getForeignTableName();
-
-                    if (!caseSensitive)
-                    {
-                        refName = refName.toUpperCase();
-                    }
-                    if ((sourceFk != null) && namesOfKnownChangedTables.contains(refName))
-                    {
-                        if (relevantFks == null)
-                        {
-                            relevantFks = new ArrayList();
-                            fksPerTable.put(name, relevantFks);
-                        }
-                        relevantFks.add(targetFk);
-                    }
-                }
-            }
-        }
-    }
-    
-    /**
-     * Processes the changes to the structure of a single table. Database-specific
-     * implementations might redefine this method, but it is usually sufficient to
-     * redefine the {@link #processTableStructureChanges(Database, Database, Table, Table, Map, List)}
-     * method instead.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param tableName    The name of the changed table
-     * @param parameters   The creation parameters for the desired table
-     * @param changes      The change objects for this table
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                String   tableName,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        Table sourceTable = currentModel.findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
-        Table targetTable = desiredModel.findTable(tableName, getPlatform().isDelimitedIdentifierModeOn());
-
-        // we're enforcing a full rebuild in case of the addition of a required
-        // column without a default value that is not autoincrement
-        boolean requiresFullRebuild = false;
-
-        for (Iterator changeIt = changes.iterator(); !requiresFullRebuild && changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                if (addColumnChange.getNewColumn().isRequired() &&
-                    (addColumnChange.getNewColumn().getDefaultValue() == null) &&
-                    !addColumnChange.getNewColumn().isAutoIncrement())
-                {
-                    requiresFullRebuild = true;
-                }
-            }
-        }
-        if (!requiresFullRebuild)
-        {
-            processTableStructureChanges(currentModel, desiredModel, sourceTable, targetTable, parameters, changes);
-        }
-
-        if (!changes.isEmpty())
-        {
-            // 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 = changes.iterator(); canMigrateData && it.hasNext();)
-            {
-                TableChange change = (TableChange)it.next();
-
-                if (change instanceof AddColumnChange)
-                {
-                    AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                    if (addColumnChange.getNewColumn().isRequired() &&
-                        !addColumnChange.getNewColumn().isAutoIncrement() &&
-                        (addColumnChange.getNewColumn().getDefaultValue() == null))
-                    {
-                        _log.warn("Data cannot be retained in table " + change.getChangedTable().getName() + 
-                                  " because of the addition of the required column " + addColumnChange.getNewColumn().getName());
-                        canMigrateData = false;
-                    }
-                }
-            }
-
-            Table realTargetTable = getRealTargetTableFor(desiredModel, sourceTable, targetTable);
-
-            if (canMigrateData)
-            {
-                Table tempTable = getTemporaryTableFor(desiredModel, targetTable);
-    
-                createTemporaryTable(desiredModel, tempTable, parameters);
-                writeCopyDataStatement(sourceTable, 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
-                dropTable(sourceTable);
-                createTable(desiredModel, realTargetTable, parameters);
-                writeCopyDataStatement(tempTable, targetTable);
-                dropTemporaryTable(desiredModel, tempTable);
-            }
-            else
-            {
-                dropTable(sourceTable);
-                createTable(desiredModel, realTargetTable, parameters);
-            }
-        }
-    }
-
-    /**
-     * Allows database-specific implementations to handle changes in a database
-     * specific manner. Any handled change should be applied to the given current
-     * model (which is a copy of the real original model) and be removed from the
-     * list of changes.<br/>
-     * In the default implementation, all {@link AddPrimaryKeyChange} changes are
-     * applied via an <code>ALTER TABLE ADD CONSTRAINT</code> statement.  
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param sourceTable  The original table
-     * @param targetTable  The desired table
-     * @param parameters   The creation parameters for the table
-     * @param changes      The change objects for the target table
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        if (changes.size() == 1)
-        {
-            TableChange change = (TableChange)changes.get(0);
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changes.clear();
-            }
-        }
-    }
-    
-    /**
-     * 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 targetModel The target database
-     * @param targetTable The target table
-     * @return The temporary table
-     */
-    protected Table getTemporaryTableFor(Database targetModel, Table targetTable) throws IOException
-    {
-        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++)
-        {
-            try
-            {
-                table.addColumn((Column)targetTable.getColumn(idx).clone());
-            }
-            catch (CloneNotSupportedException ex)
-            {
-                throw new DdlUtilsException(ex);
-            }
-        }
-
-        return table;
+        createForeignKeys(database);
     }
 
     /**
@@ -1079,55 +445,6 @@
     }
 
     /**
-     * Creates the target table object that differs from the given target table only in the
-     * indices. More specifically, only those indices are used that have not changed.
-     * 
-     * @param targetModel The target database
-     * @param sourceTable The source table
-     * @param targetTable The target table
-     * @return The table
-     */
-    protected Table getRealTargetTableFor(Database targetModel, Table sourceTable, Table targetTable) throws IOException
-    {
-        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++)
-        {
-            try
-            {
-                table.addColumn((Column)targetTable.getColumn(idx).clone());
-            }
-            catch (CloneNotSupportedException ex)
-            {
-                throw new DdlUtilsException(ex);
-            }
-        }
-
-        boolean caseSensitive = getPlatform().isDelimitedIdentifierModeOn();
-
-        for (int idx = 0; idx < targetTable.getIndexCount(); idx++)
-        {
-            Index targetIndex = targetTable.getIndex(idx);
-            Index sourceIndex = sourceTable.findIndex(targetIndex.getName(), caseSensitive);
-
-            if (sourceIndex != null)
-            {
-                if ((caseSensitive  && sourceIndex.equals(targetIndex)) ||
-                    (!caseSensitive && sourceIndex.equalsIgnoreCase(targetIndex)))
-                {
-                    table.addIndex(targetIndex);
-                }
-            }
-        }
-
-        return table;
-    }
-
-    /**
      * Writes a statement that copies the data from the source to the target table. Note
      * that this copies only those columns that are in both tables.
      * Database-specific implementations might redefine this method though they usually
@@ -1136,7 +453,7 @@
      * @param sourceTable The source table
      * @param targetTable The target table
      */
-    protected void writeCopyDataStatement(Table sourceTable, Table targetTable) throws IOException
+    protected void copyData(Table sourceTable, Table targetTable) throws IOException
     {
         ListOrderedMap columns = new ListOrderedMap();
 
@@ -1195,79 +512,6 @@
     }
 
     /**
-     * Processes the addition of a primary key to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database            currentModel,
-                                 Database            desiredModel,
-                                 AddPrimaryKeyChange change) throws IOException
-    {
-        writeExternalPrimaryKeysCreateStmt(change.getChangedTable(), change.getPrimaryKeyColumns());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Searches in the given table for a corresponding foreign key. If the given key
-     * has no name, then a foreign key to the same table with the same columns in the
-     * same order is searched. If the given key has a name, then the a corresponding
-     * key also needs to have the same name, or no name at all, but not a different one. 
-     * 
-     * @param table The table to search in
-     * @param fk    The original foreign key
-     * @return The corresponding foreign key if found
-     */
-    protected ForeignKey findCorrespondingForeignKey(Table table, ForeignKey fk)
-    {
-        boolean     caseMatters = getPlatform().isDelimitedIdentifierModeOn();
-        boolean     checkFkName = (fk.getName() != null) && (fk.getName().length() > 0);
-        Reference[] refs        = fk.getReferences();
-        ArrayList   curRefs     = new ArrayList();
-
-        for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++)
-        {
-            ForeignKey curFk          = table.getForeignKey(fkIdx);
-            boolean    checkCurFkName = checkFkName &&
-                                        (curFk.getName() != null) && (curFk.getName().length() > 0);
-
-            if ((!checkCurFkName || areEqual(fk.getName(), curFk.getName(), caseMatters)) &&
-                areEqual(fk.getForeignTableName(), curFk.getForeignTableName(), caseMatters))
-            {
-                curRefs.clear();
-                CollectionUtils.addAll(curRefs, curFk.getReferences());
-
-                // the order is not fixed, so we have to take this long way
-                if (curRefs.size() == refs.length)
-                {
-                    for (int refIdx = 0; refIdx < refs.length; refIdx++)
-                    {
-                        boolean found = false;
-
-                        for (int curRefIdx = 0; !found && (curRefIdx < curRefs.size()); curRefIdx++)
-                        {
-                            Reference curRef = (Reference)curRefs.get(curRefIdx);
-
-                            if ((caseMatters  && refs[refIdx].equals(curRef)) ||
-                                (!caseMatters && refs[refIdx].equalsIgnoreCase(curRef)))
-                            {
-                                curRefs.remove(curRefIdx);
-                                found = true;
-                            }
-                        }
-                    }
-                    if (curRefs.isEmpty())
-                    {
-                        return curFk;
-                    }
-                }
-            }
-        }
-        return null;
-    } 
-
-    /**
      * Compares the two strings.
      * 
      * @param string1     The first string
@@ -1308,11 +552,102 @@
 
         if (!getPlatformInfo().isPrimaryKeyEmbedded())
         {
-            writeExternalPrimaryKeysCreateStmt(table, table.getPrimaryKeyColumns());
+            createPrimaryKey(table, table.getPrimaryKeyColumns());
         }
         if (!getPlatformInfo().isIndicesEmbedded())
         {
-            writeExternalIndicesCreateStmt(table);
+            createIndexes(table);
+        }
+    }
+
+    /**
+     * Writes the primary key constraints of the table as alter table statements.
+     * 
+     * @param table             The table
+     * @param primaryKeyColumns The primary key columns 
+     */
+    public void createPrimaryKey(Table table, Column[] primaryKeyColumns) throws IOException
+    {
+        if ((primaryKeyColumns.length > 0) && shouldGeneratePrimaryKeys(primaryKeyColumns))
+        {
+            print("ALTER TABLE ");
+            printlnIdentifier(getTableName(table));
+            printIndent();
+            print("ADD CONSTRAINT ");
+            printIdentifier(getConstraintName(null, table, "PK", null));
+            print(" ");
+            writePrimaryKeyStmt(table, primaryKeyColumns);
+            printEndOfStatement();
+        }
+    }
+
+    /**
+     * Writes the indexes for the given table using external index creation statements.
+     * 
+     * @param table The table
+     */
+    public void createIndexes(Table table) throws IOException
+    {
+        for (int idx = 0; idx < table.getIndexCount(); idx++)
+        {
+            Index index = table.getIndex(idx);
+
+            if (!index.isUnique() && !getPlatformInfo().isIndicesSupported())
+            {
+                throw new ModelException("Platform does not support non-unique indices");
+            }
+            createIndex(table, index);
+        }
+    }
+
+    /**
+     * Writes the given index for the table using an external index creation statement.
+     * 
+     * @param table The table
+     * @param index The index
+     */
+    public void createIndex(Table table, Index index) throws IOException
+    {
+        if (!getPlatformInfo().isIndicesSupported())
+        {
+            throw new DdlUtilsException("This platform does not support indexes");
+        }
+        else if (index.getName() == null)
+        {
+            _log.warn("Cannot write unnamed index " + index);
+        }
+        else
+        {
+            print("CREATE");
+            if (index.isUnique())
+            {
+                print(" UNIQUE");
+            }
+            print(" INDEX ");
+            printIdentifier(getIndexName(index));
+            print(" ON ");
+            printIdentifier(getTableName(table));
+            print(" (");
+
+            for (int idx = 0; idx < index.getColumnCount(); idx++)
+            {
+                IndexColumn idxColumn = index.getColumn(idx);
+                Column      col       = table.findColumn(idxColumn.getName());
+
+                if (col == null)
+                {
+                    // would get null pointer on next line anyway, so throw exception
+                    throw new ModelException("Invalid column '" + idxColumn.getName() + "' on index " + index.getName() + " for table " + table.getName());
+                }
+                if (idx > 0)
+                {
+                    print(", ");
+                }
+                printIdentifier(getColumnName(col));
+            }
+
+            print(")");
+            printEndOfStatement();
         }
     }
 
@@ -1321,11 +656,11 @@
      * 
      * @param database The database
      */
-    public void createExternalForeignKeys(Database database) throws IOException
+    public void createForeignKeys(Database database) throws IOException
     {
         for (int idx = 0; idx < database.getTableCount(); idx++)
         {
-            createExternalForeignKeys(database, database.getTable(idx));
+            createForeignKeys(database, database.getTable(idx));
         }
     }
 
@@ -1335,18 +670,67 @@
      * @param database The database model
      * @param table    The table
      */
-    public void createExternalForeignKeys(Database database, Table table) throws IOException
+    public void createForeignKeys(Database database, Table table) throws IOException
     {
-        if (!getPlatformInfo().isForeignKeysEmbedded())
+        for (int idx = 0; idx < table.getForeignKeyCount(); idx++)
         {
-            for (int idx = 0; idx < table.getForeignKeyCount(); idx++)
-            {
-                writeExternalForeignKeyCreateStmt(database, table, table.getForeignKey(idx));
-            }
+            createForeignKey(database, table, table.getForeignKey(idx));
         }
     }
 
     /**
+     * Writes a single foreign key constraint using a alter table statement.
+     * 
+     * @param database   The database model
+     * @param table      The table 
+     * @param foreignKey The foreign key
+     */
+    public void createForeignKey(Database database, Table table, ForeignKey foreignKey) throws IOException
+    {
+        if (getPlatformInfo().isForeignKeysEmbedded())
+        {
+            throw new DdlUtilsException("This platform does not supported the external creation of foreign keys");
+        }
+        else if (foreignKey.getForeignTableName() == null)
+        {
+            _log.warn("Foreign key table is null for key " + foreignKey);
+        }
+        else
+        {
+            writeTableAlterStmt(table);
+
+            print("ADD CONSTRAINT ");
+            printIdentifier(getForeignKeyName(table, foreignKey));
+            print(" FOREIGN KEY (");
+            writeLocalReferences(foreignKey);
+            print(") REFERENCES ");
+            printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName())));
+            print(" (");
+            writeForeignReferences(foreignKey);
+            print(")");
+            writeForeignKeyOnDeleteAction(table, foreignKey);
+            writeForeignKeyOnUpdateAction(table, foreignKey);
+            printEndOfStatement();
+        }
+    }
+
+    /**
+     * Prints the SQL for adding a column to a table.
+     * 
+     * @param table     The table
+     * @param newColumn The new column
+     */
+    public void addColumn(Table table, Column newColumn) throws IOException
+    {
+        print("ALTER TABLE ");
+        printlnIdentifier(getTableName(table));
+        printIndent();
+        print("ADD COLUMN ");
+        writeColumn(table, newColumn);
+        printEndOfStatement();
+    }
+
+    /**
      * Outputs the DDL required to drop the database.
      * 
      * @param database The database 
@@ -1361,7 +745,7 @@
             if ((table.getName() != null) &&
                 (table.getName().length() > 0))
             {
-                dropExternalForeignKeys(table);
+                dropForeignKeys(table);
             }
         }
 
@@ -1401,12 +785,12 @@
             {
                 if (fks[fkIdx].getForeignTable().equals(table))
                 {
-                    writeExternalForeignKeyDropStmt(otherTable, fks[fkIdx]);
+                    dropForeignKey(otherTable, fks[fkIdx]);
                 }
             }
         }
         // and the foreign keys from the table
-        dropExternalForeignKeys(table);
+        dropForeignKeys(table);
 
         writeTableComment(table);
         dropTable(table);
@@ -1431,18 +815,30 @@
      * 
      * @param table The table
      */
-    public void dropExternalForeignKeys(Table table) throws IOException
+    public void dropForeignKeys(Table table) throws IOException
     {
-        if (!getPlatformInfo().isForeignKeysEmbedded())
+        for (int idx = 0; idx < table.getForeignKeyCount(); idx++)
         {
-            for (int idx = 0; idx < table.getForeignKeyCount(); idx++)
-            {
-                writeExternalForeignKeyDropStmt(table, table.getForeignKey(idx));
-            }
+            dropForeignKey(table, table.getForeignKey(idx));
         }
     }
 
     /**
+     * Generates the statement to drop a foreignkey constraint from the database using an
+     * alter table statement.
+     *
+     * @param table      The table 
+     * @param foreignKey The foreign key
+     */
+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException
+    {
+        writeTableAlterStmt(table);
+        print("DROP CONSTRAINT ");
+        printIdentifier(getForeignKeyName(table, foreignKey));
+        printEndOfStatement();
+    }
+
+    /**
      * Creates the SQL for inserting an object into the specified table.
      * If values are given then a concrete insert statement is created, otherwise an
      * insert statement usable in a prepared statement is build.
@@ -2313,27 +1709,6 @@
     }
 
     /**
-     * Writes the primary key constraints of the table as alter table statements.
-     * 
-     * @param table             The table
-     * @param primaryKeyColumns The primary key columns 
-     */
-    protected void writeExternalPrimaryKeysCreateStmt(Table table, Column[] primaryKeyColumns) throws IOException
-    {
-        if ((primaryKeyColumns.length > 0) && shouldGeneratePrimaryKeys(primaryKeyColumns))
-        {
-            print("ALTER TABLE ");
-            printlnIdentifier(getTableName(table));
-            printIndent();
-            print("ADD CONSTRAINT ");
-            printIdentifier(getConstraintName(null, table, "PK", null));
-            print(" ");
-            writePrimaryKeyStmt(table, primaryKeyColumns);
-            printEndOfStatement();
-        }
-    }
-
-    /**
      * Determines whether we should generate a primary key constraint for the given
      * primary key columns.
      * 
@@ -2377,25 +1752,6 @@
     }
 
     /**
-     * Writes the indexes of the given table.
-     * 
-     * @param table The table
-     */
-    protected void writeExternalIndicesCreateStmt(Table table) throws IOException
-    {
-        for (int idx = 0; idx < table.getIndexCount(); idx++)
-        {
-            Index index = table.getIndex(idx);
-
-            if (!index.isUnique() && !getPlatformInfo().isIndicesSupported())
-            {
-                throw new ModelException("Platform does not support non-unique indices");
-            }
-            writeExternalIndexCreateStmt(table, index);
-        }
-    }
-
-    /**
      * Writes the indexes embedded within the create table statement.
      * 
      * @param table The table
@@ -2413,56 +1769,6 @@
     }
 
     /**
-     * Writes the given index of the table.
-     * 
-     * @param table The table
-     * @param index The index
-     */
-    protected void writeExternalIndexCreateStmt(Table table, Index index) throws IOException
-    {
-        if (getPlatformInfo().isIndicesSupported())
-        {
-            if (index.getName() == null)
-            {
-                _log.warn("Cannot write unnamed index " + index);
-            }
-            else
-            {
-                print("CREATE");
-                if (index.isUnique())
-                {
-                    print(" UNIQUE");
-                }
-                print(" INDEX ");
-                printIdentifier(getIndexName(index));
-                print(" ON ");
-                printIdentifier(getTableName(table));
-                print(" (");
-    
-                for (int idx = 0; idx < index.getColumnCount(); idx++)
-                {
-                    IndexColumn idxColumn = index.getColumn(idx);
-                    Column      col       = table.findColumn(idxColumn.getName());
-    
-                    if (col == null)
-                    {
-                        // would get null pointer on next line anyway, so throw exception
-                        throw new ModelException("Invalid column '" + idxColumn.getName() + "' on index " + index.getName() + " for table " + table.getName());
-                    }
-                    if (idx > 0)
-                    {
-                        print(", ");
-                    }
-                    printIdentifier(getColumnName(col));
-                }
-    
-                print(")");
-                printEndOfStatement();
-            }
-        }
-    }
-
-    /**
      * Writes the given embedded index of the table.
      * 
      * @param table The table
@@ -2511,7 +1817,7 @@
      * @param table The table the index is on
      * @param index The index to drop
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         if (getPlatformInfo().isAlterTableForDropUsed())
         {
@@ -2567,38 +1873,6 @@
     }
 
     /**
-     * Writes a single foreign key constraint using a alter table statement.
-     * 
-     * @param database   The database model
-     * @param table      The table 
-     * @param foreignKey The foreign key
-     */
-    protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ForeignKey foreignKey) throws IOException
-    {
-        if (foreignKey.getForeignTableName() == null)
-        {
-            _log.warn("Foreign key table is null for key " + foreignKey);
-        }
-        else
-        {
-            writeTableAlterStmt(table);
-
-            print("ADD CONSTRAINT ");
-            printIdentifier(getForeignKeyName(table, foreignKey));
-            print(" FOREIGN KEY (");
-            writeLocalReferences(foreignKey);
-            print(") REFERENCES ");
-            printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName())));
-            print(" (");
-            writeForeignReferences(foreignKey);
-            print(")");
-            writeForeignKeyOnDeleteAction(table, foreignKey);
-            writeForeignKeyOnUpdateAction(table, foreignKey);
-            printEndOfStatement();
-        }
-    }
-
-    /**
      * Writes a list of local references for the given foreign key.
      * 
      * @param key The foreign key
@@ -2702,21 +1976,6 @@
         }
     }
 
-    /**
-     * Generates the statement to drop a foreignkey constraint from the database using an
-     * alter table statement.
-     *
-     * @param table      The table 
-     * @param foreignKey The foreign key
-     */
-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException
-    {
-        writeTableAlterStmt(table);
-        print("DROP CONSTRAINT ");
-        printIdentifier(getForeignKeyName(table, foreignKey));
-        printEndOfStatement();
-    }
-
     //
     // Helper methods
     //
diff --git a/src/java/org/apache/ddlutils/platform/axion/AxionBuilder.java b/src/java/org/apache/ddlutils/platform/axion/AxionBuilder.java
index 9e343c4..2d0d930 100644
--- a/src/java/org/apache/ddlutils/platform/axion/AxionBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/axion/AxionBuilder.java
@@ -66,7 +66,7 @@
     /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         print("DROP INDEX ");
         printIdentifier(getIndexName(index));
diff --git a/src/java/org/apache/ddlutils/platform/cloudscape/CloudscapePlatform.java b/src/java/org/apache/ddlutils/platform/cloudscape/CloudscapePlatform.java
index 06d1fa5..aa4379e 100644
--- a/src/java/org/apache/ddlutils/platform/cloudscape/CloudscapePlatform.java
+++ b/src/java/org/apache/ddlutils/platform/cloudscape/CloudscapePlatform.java
@@ -47,6 +47,9 @@
 

         info.setMaxIdentifierLength(128);

         info.setSystemForeignKeyIndicesAlwaysNonUnique(true);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

+        info.setIdentityColumnAutomaticallyRequired(true);

+

         // BINARY and VARBINARY will also be handled by CloudscapeBuilder.getSqlType

         info.addNativeTypeMapping(Types.ARRAY,         "BLOB",                     Types.BLOB);

         info.addNativeTypeMapping(Types.BINARY,        "CHAR {0} FOR BIT DATA");

diff --git a/src/java/org/apache/ddlutils/platform/db2/Db2Builder.java b/src/java/org/apache/ddlutils/platform/db2/Db2Builder.java
index e9fc2db..f56aa4c 100644
--- a/src/java/org/apache/ddlutils/platform/db2/Db2Builder.java
+++ b/src/java/org/apache/ddlutils/platform/db2/Db2Builder.java
@@ -21,19 +21,9 @@
 
 import java.io.IOException;
 import java.sql.Types;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
-import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Index;
 import org.apache.ddlutils.model.Table;
 import org.apache.ddlutils.model.TypeMap;
@@ -91,9 +81,39 @@
     }
 
     /**
+     * Generates the SQL to drop a column from a table.
+     * 
+     * @param table  The table where to drop the column from
+     * @param column The column to drop
+     */
+    public void dropColumn(Table table, Column column) throws IOException
+    {
+        print("ALTER TABLE ");
+        printlnIdentifier(getTableName(table));
+        printIndent();
+        print("DROP COLUMN ");
+        printIdentifier(getColumnName(column));
+        printEndOfStatement();
+    }
+
+    /**
+     * Writes the SQL for dropping the primary key of the given table.
+     * 
+     * @param table The table
+     */
+    public void dropPrimaryKey(Table table) throws IOException
+    {
+        print("ALTER TABLE ");
+        printlnIdentifier(getTableName(table));
+        printIndent();
+        print("DROP PRIMARY KEY");
+        printEndOfStatement();
+    }
+
+    /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         // Index names in DB2 are unique to a schema and hence Derby does not
         // use the ON <tablename> clause
@@ -139,135 +159,4 @@
             print(")");
         }
     }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // DB2 provides only limited ways to alter a column, so we don't use them
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // DB2 can only add not insert columns
-                // Also, DB2 does not allow the GENERATED BY DEFAULT AS IDENTITY clause in
-                // the ALTER TABLE ADD COLUMN statement, so we have to rebuild the table instead
-                if ((addColumnChange.getNextColumn() == null) && !addColumnChange.getNewColumn().isAutoIncrement())
-                {
-                    processChange(currentModel, desiredModel, addColumnChange);
-                    changeIt.remove();
-                }
-            }
-        }
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (PrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-        }
-    }
-
-    /**
-     * Processes the addition of a column to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("ADD COLUMN ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
-        printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the removal of a column from a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
-        printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the removal of a primary key from a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("DROP PRIMARY KEY");
-        printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the change of the primary key of a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database         currentModel,
-                                 Database         desiredModel,
-                                 PrimaryKeyChange change) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("DROP PRIMARY KEY");
-        printEndOfStatement();
-        writeExternalPrimaryKeysCreateStmt(change.getChangedTable(), change.getNewPrimaryKeyColumns());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
 }
diff --git a/src/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java b/src/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
index aa89465..d11e8bf 100644
--- a/src/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
+++ b/src/java/org/apache/ddlutils/platform/db2/Db2ModelReader.java
@@ -19,8 +19,10 @@
  * under the License.

  */

 

+import java.sql.PreparedStatement;

 import java.sql.ResultSet;

 import java.sql.SQLException;

+import java.sql.Statement;

 import java.sql.Types;

 import java.util.HashSet;

 import java.util.Map;

@@ -98,7 +100,7 @@
         if (table != null)

         {

             // Db2 does not return the auto-increment status via the database metadata

-            determineAutoIncrementFromResultSetMetaData(table, table.getColumns());

+            determineAutoIncrementColumns(table);

         }

         return table;

 	}

@@ -173,6 +175,42 @@
 		}

 		return column;

 	}

+    /**

+     * Helper method that determines the auto increment status using Firebird's system tables.

+     *

+     * @param table The table

+     */

+    protected void determineAutoIncrementColumns(Table table) throws SQLException

+    {

+        PreparedStatement stmt = null;

+

+        try

+        {

+            stmt = getConnection().prepareStatement("SELECT COLNAME FROM SYSCAT.COLUMNS WHERE TABNAME = ? AND IDENTITY = 'Y' AND HIDDEN != 'S'");

+            stmt.setString(1, table.getName());

+

+            ResultSet rs = stmt.executeQuery();

+

+            while (rs.next())

+            {

+                String colName = rs.getString(1).trim();

+                Column column  = table.findColumn(colName, getPlatform().isDelimitedIdentifierModeOn());

+

+                if (column != null)

+                {

+                    column.setAutoIncrement(true);

+                }

+            }

+            rs.close();

+        }

+        finally

+        {

+            if (stmt != null)

+            {

+                stmt.close();

+            }

+        }

+    }

 

 	/**

      * {@inheritDoc}

diff --git a/src/java/org/apache/ddlutils/platform/db2/Db2Platform.java b/src/java/org/apache/ddlutils/platform/db2/Db2Platform.java
index b322ad1..d22f327 100644
--- a/src/java/org/apache/ddlutils/platform/db2/Db2Platform.java
+++ b/src/java/org/apache/ddlutils/platform/db2/Db2Platform.java
@@ -19,10 +19,24 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Types;

 

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

+import org.apache.ddlutils.util.StringUtils;

 

 /**

  * The DB2 platform implementation.

@@ -58,6 +72,7 @@
         PlatformInfo info = getPlatformInfo();

 

         info.setMaxIdentifierLength(18);

+        info.setIdentityColumnAutomaticallyRequired(true);

 

         // the BINARY types are also handled by Db2Builder.getSqlType(Column)

         info.addNativeTypeMapping(Types.ARRAY,         "BLOB",                      Types.BLOB);

@@ -91,4 +106,109 @@
     {

         return DATABASENAME;

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        ModelComparator comparator = super.getModelComparator();

+

+        comparator.setCanDropPrimaryKeyColumns(false);

+        comparator.setGeneratePrimaryKeyChanges(false);

+        return comparator;

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof PrimaryKeyChange) ||

+                    (change instanceof RemovePrimaryKeyChange))

+                {

+                    return true;

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // DB2 cannot add IDENTITY columns, and required columns need a default value

+                    return !addColumnChange.getNewColumn().isAutoIncrement() &&

+                           (!addColumnChange.getNewColumn().isRequired() || !StringUtils.isEmpty(addColumnChange.getNewColumn().getDefaultValue()));

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((Db2Builder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((Db2Builder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+    

+    /**

+     * Processes the change of the primary key 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,

+                              PrimaryKeyChange   change) throws IOException

+    {

+        Table    changedTable     = findChangedTable(currentModel, change);

+        String[] newPKColumnNames = change.getNewPrimaryKeyColumns();

+        Column[] newPKColumns     = new Column[newPKColumnNames.length];

+

+        for (int colIdx = 0; colIdx < newPKColumnNames.length; colIdx++)

+        {

+            newPKColumns[colIdx] = changedTable.findColumn(newPKColumnNames[colIdx], isDelimitedIdentifierModeOn());

+        }

+        ((Db2Builder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        getSqlBuilder().createPrimaryKey(changedTable, newPKColumns);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/db2/Db2v8Builder.java b/src/java/org/apache/ddlutils/platform/db2/Db2v8Builder.java
index 9b2a196..8253dd8 100644
--- a/src/java/org/apache/ddlutils/platform/db2/Db2v8Builder.java
+++ b/src/java/org/apache/ddlutils/platform/db2/Db2v8Builder.java
@@ -19,7 +19,11 @@
  * under the License.

  */

 

+import java.io.IOException;

+

 import org.apache.ddlutils.Platform;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Table;

 

 /**

  * The SQL Builder for DB2 v8 and above.

@@ -37,4 +41,19 @@
     {

         super(platform);

     }

+

+    /**

+     * Generates the SQL to drop a column from a table.

+     * 

+     * @param table  The table where to drop the column from

+     * @param column The column to drop

+     */

+    public void dropColumn(Table table, Column column) throws IOException

+    {

+        super.dropColumn(table, column);

+        print("CALL ADMIN_CMD('REORG TABLE ");

+        printIdentifier(getTableName(table));

+        print("')");

+        printEndOfStatement();

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/derby/DerbyBuilder.java b/src/java/org/apache/ddlutils/platform/derby/DerbyBuilder.java
index d740261..57b284f 100644
--- a/src/java/org/apache/ddlutils/platform/derby/DerbyBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/derby/DerbyBuilder.java
@@ -21,15 +21,9 @@
 

 import java.io.IOException;

 import java.sql.Types;

-import java.util.Iterator;

-import java.util.List;

-import java.util.Map;

 

 import org.apache.ddlutils.Platform;

-import org.apache.ddlutils.alteration.AddColumnChange;

-import org.apache.ddlutils.alteration.TableChange;

 import org.apache.ddlutils.model.Column;

-import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Index;

 import org.apache.ddlutils.model.Table;

 import org.apache.ddlutils.model.TypeMap;

@@ -80,7 +74,7 @@
     /**

      * {@inheritDoc}

      */

-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException

+    public void dropIndex(Table table, Index index) throws IOException

     {

         // Index names in Derby are unique to a schema and hence Derby does not

         // use the ON <tablename> clause

@@ -117,51 +111,4 @@
             print(")");

         }

     }

-

-    /**

-     * {@inheritDoc}

-     */

-    protected void processTableStructureChanges(Database currentModel, Database desiredModel, Table sourceTable, Table targetTable, Map parameters, List changes) throws IOException

-    {

-        // Derby provides a way to alter the size of a column but it is limited

-        // (no pk or fk columns, only for VARCHAR columns), so we don't use it

-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)

-        {

-            TableChange change = (TableChange)changeIt.next();

-

-            if (change instanceof AddColumnChange)

-            {

-                AddColumnChange addColumnChange = (AddColumnChange)change;

-

-                // Derby can only add not insert columns, and the columns

-                // cannot be identity columns

-                if (addColumnChange.isAtEnd() && !addColumnChange.getNewColumn().isAutoIncrement())

-                {

-                    processChange(currentModel, desiredModel, addColumnChange);

-                    changeIt.remove();

-                }

-            }

-        }

-        super.processTableStructureChanges(currentModel, desiredModel, sourceTable, targetTable, parameters, changes);

-    }

-

-    /**

-     * Processes the addition of a column to a table.

-     * 

-     * @param currentModel The current database schema

-     * @param desiredModel The desired database schema

-     * @param change       The change object

-     */

-    protected void processChange(Database        currentModel,

-                                 Database        desiredModel,

-                                 AddColumnChange change) throws IOException

-    {

-        print("ALTER TABLE ");

-        printlnIdentifier(getTableName(change.getChangedTable()));

-        printIndent();

-        print("ADD COLUMN ");

-        writeColumn(change.getChangedTable(), change.getNewColumn());

-        printEndOfStatement();

-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());

-    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/derby/DerbyPlatform.java b/src/java/org/apache/ddlutils/platform/derby/DerbyPlatform.java
index d0a162f..91ee0e4 100644
--- a/src/java/org/apache/ddlutils/platform/derby/DerbyPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/derby/DerbyPlatform.java
@@ -27,6 +27,11 @@
 import java.util.Map;

 

 import org.apache.ddlutils.DatabaseOperationException;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.cloudscape.CloudscapePlatform;

 

 /**

@@ -131,4 +136,27 @@
             throw new UnsupportedOperationException("Unable to create a Derby database via the driver "+jdbcDriverClassName);

         }

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                // Derby cannot add IDENTITY columns

+                if ((change instanceof AddColumnChange) &&

+                    ((AddColumnChange)change).getNewColumn().isAutoIncrement())

+                {

+                    return false;

+                }

+                else

+                {

+                    return super.isSupported(intermediateTable, change);

+                }

+            }

+        };

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/firebird/FirebirdBuilder.java b/src/java/org/apache/ddlutils/platform/firebird/FirebirdBuilder.java
index 6042f7e..b0adac8 100644
--- a/src/java/org/apache/ddlutils/platform/firebird/FirebirdBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/firebird/FirebirdBuilder.java
@@ -21,15 +21,9 @@
 

 import java.io.IOException;

 import java.sql.Types;

-import java.util.Iterator;

-import java.util.List;

 import java.util.Map;

 

 import org.apache.ddlutils.Platform;

-import org.apache.ddlutils.alteration.AddColumnChange;

-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

-import org.apache.ddlutils.alteration.RemoveColumnChange;

-import org.apache.ddlutils.alteration.TableChange;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Index;

@@ -198,18 +192,18 @@
      * 

      * {@inheritDoc}

      */

-    public void createExternalForeignKeys(Database database) throws IOException

+    public void createForeignKeys(Database database) throws IOException

     {

         for (int idx = 0; idx < database.getTableCount(); idx++)

         {

-            createExternalForeignKeys(database, database.getTable(idx));

+            createForeignKeys(database, database.getTable(idx));

         }

     }

 

     /**

      * {@inheritDoc}

      */

-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException

+    public void dropIndex(Table table, Index index) throws IOException

     {

         // Index names in Firebird are unique to a schema and hence Firebird does not

         // use the ON <tablename> clause

@@ -219,133 +213,59 @@
     }

 

     /**

-     * {@inheritDoc}

-     */

-    protected void processTableStructureChanges(Database currentModel, Database desiredModel, Table sourceTable, Table targetTable, Map parameters, List changes) throws IOException

-    {

-    	// TODO: Dropping of primary keys is currently not supported because we cannot

-    	//       determine the pk constraint names and drop them in one go

-    	//       (We could used a stored procedure if Firebird would allow them to use DDL)

-    	//       This will be easier once named primary keys are supported

-        boolean pkColumnAdded = false;

-

-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)

-        {

-            TableChange change = (TableChange)changeIt.next();

-

-            if (change instanceof AddColumnChange)

-            {

-            	AddColumnChange addColumnChange = (AddColumnChange)change;

-

-            	// TODO: we cannot add columns to the primary key this way

-            	//       because we would have to drop the pk first and then

-            	//       add a new one afterwards which is not supported yet

-            	if (addColumnChange.getNewColumn().isPrimaryKey())

-                {

-                    pkColumnAdded = true;   

-                }

-                else

-            	{

-	            	processChange(currentModel, desiredModel, addColumnChange);

-	                changeIt.remove();

-            	}

-            }

-            else if (change instanceof RemoveColumnChange)

-            {

-            	RemoveColumnChange removeColumnChange = (RemoveColumnChange)change;

-

-            	// TODO: we cannot drop primary key columns this way

-            	//       because we would have to drop the pk first and then

-            	//       add a new one afterwards which is not supported yet

-            	if (!removeColumnChange.getChangedColumn().isPrimaryKey())

-            	{

-	            	processChange(currentModel, desiredModel, removeColumnChange);

-	                changeIt.remove();

-            	}

-            }

-        }

-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)

-        {

-            TableChange change = (TableChange)changeIt.next();

-

-            // we can only add a primary key if all columns are present in the table

-            // i.e. none was added during this alteration

-            if ((change instanceof AddPrimaryKeyChange) && !pkColumnAdded)

-            {

-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);

-                changeIt.remove();

-            }

-        }

-    }

-

-    /**

-     * Processes the addition of a column to a table.

+     * Writes the SQL to add/insert a column.

      * 

-     * @param currentModel The current database schema

-     * @param desiredModel The desired database schema

-     * @param change       The change object

+     * @param model      The database model

+     * @param table      The table

+     * @param newColumn  The new column

+     * @param prevColumn The column after which the new column shall be added; <code>null</code>

+     *                   if the new column is to be inserted at the beginning

      */

-    protected void processChange(Database        currentModel,

-                                 Database        desiredModel,

-                                 AddColumnChange change) throws IOException

+    public void insertColumn(Database model, Table table, Column newColumn, Column prevColumn) throws IOException

     {

         print("ALTER TABLE ");

-        printlnIdentifier(getTableName(change.getChangedTable()));

+        printlnIdentifier(getTableName(table));

         printIndent();

         print("ADD ");

-        writeColumn(change.getChangedTable(), change.getNewColumn());

+        writeColumn(table, newColumn);

         printEndOfStatement();

 

-        Table curTable = currentModel.findTable(change.getChangedTable().getName(), getPlatform().isDelimitedIdentifierModeOn());

-

-        if (!change.isAtEnd())

+        if (prevColumn != null)

         {

-            Column prevColumn = change.getPreviousColumn();

-

-            if (prevColumn != null)

-            {

-            	// we need the corresponding column object from the current table

-            	prevColumn = curTable.findColumn(prevColumn.getName(), getPlatform().isDelimitedIdentifierModeOn());

-            }

             // Even though Firebird can only add columns, we can move them later on

             print("ALTER TABLE ");

-            printlnIdentifier(getTableName(change.getChangedTable()));

+            printlnIdentifier(getTableName(table));

             printIndent();

             print("ALTER ");

-            printIdentifier(getColumnName(change.getNewColumn()));

+            printIdentifier(getColumnName(newColumn));

             print(" POSITION ");

             // column positions start at 1 in Firebird

-            print(prevColumn == null ? "1" : String.valueOf(curTable.getColumnIndex(prevColumn) + 2));

+            print(String.valueOf(table.getColumnIndex(prevColumn) + 2));

             printEndOfStatement();

         }

-        if (change.getNewColumn().isAutoIncrement())

+        if (newColumn.isAutoIncrement())

         {

-            writeAutoIncrementCreateStmts(currentModel, curTable, change.getNewColumn());

+            writeAutoIncrementCreateStmts(model, table, newColumn);

         }

-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());

     }

 

     /**

-     * Processes the removal of a column from a table.

+     * Writes the SQL to drop a column.

      * 

-     * @param currentModel The current database schema

-     * @param desiredModel The desired database schema

-     * @param change       The change object

+     * @param table  The table

+     * @param column The column to drop

      */

-    protected void processChange(Database           currentModel,

-                                 Database           desiredModel,

-                                 RemoveColumnChange change) throws IOException

+    public void dropColumn(Table table, Column column) throws IOException

     {

-        if (change.getChangedColumn().isAutoIncrement())

+        if (column.isAutoIncrement())

         {

-            writeAutoIncrementDropStmts(change.getChangedTable(), change.getChangedColumn());

+            writeAutoIncrementDropStmts(table, column);

         }

         print("ALTER TABLE ");

-        printlnIdentifier(getTableName(change.getChangedTable()));

+        printlnIdentifier(getTableName(table));

         printIndent();

         print("DROP ");

-        printIdentifier(getColumnName(change.getChangedColumn()));

+        printIdentifier(getColumnName(column));

         printEndOfStatement();

-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());

     }

 }

diff --git a/src/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java b/src/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
index 5df64c4..e51a2f9 100644
--- a/src/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
+++ b/src/java/org/apache/ddlutils/platform/firebird/FirebirdModelReader.java
@@ -40,6 +40,7 @@
 import org.apache.ddlutils.model.TypeMap;

 import org.apache.ddlutils.platform.DatabaseMetaDataWrapper;

 import org.apache.ddlutils.platform.JdbcModelReader;

+import org.apache.ddlutils.platform.MetaDataColumnDescriptor;

 

 /**

  * The Jdbc Model Reader for Firebird.

@@ -314,8 +315,8 @@
             {

                 Map values = readColumns(indexData, getColumnsForIndex());

 

-                // we have to reverse the meaning of the unique flag

-                values.put("NON_UNIQUE", Boolean.FALSE.equals(values.get("NON_UNIQUE")) ? Boolean.TRUE : Boolean.FALSE);

+                // we have to reverse the meaning of the unique flag; also, null means false

+                values.put("NON_UNIQUE", (values.get("NON_UNIQUE") == null) || Boolean.FALSE.equals(values.get("NON_UNIQUE")) ? Boolean.TRUE : Boolean.FALSE);

                 // and trim the names

                 values.put("INDEX_NAME",  ((String)values.get("INDEX_NAME")).trim());

                 values.put("TABLE_NAME",  ((String)values.get("TABLE_NAME")).trim());

diff --git a/src/java/org/apache/ddlutils/platform/firebird/FirebirdPlatform.java b/src/java/org/apache/ddlutils/platform/firebird/FirebirdPlatform.java
index 5cd98fe..ae05a3a 100644
--- a/src/java/org/apache/ddlutils/platform/firebird/FirebirdPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/firebird/FirebirdPlatform.java
@@ -19,9 +19,25 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Types;

+import java.util.HashSet;

+import java.util.Iterator;

+import java.util.List;

 

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -48,6 +64,7 @@
 

         info.setMaxIdentifierLength(31);

         info.setSystemForeignKeyIndicesAlwaysNonUnique(true);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

         info.setCommentPrefix("/*");

         info.setCommentSuffix("*/");

 

@@ -88,4 +105,129 @@
     {

         return DATABASENAME;

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        ModelComparator comparator = super.getModelComparator();

+

+        comparator.setCanDropPrimaryKeyColumns(false);

+        return comparator;

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            public boolean areSupported(Table intermediateTable, List changes)

+            {

+                // Firebird does support adding a primary key, but only if none of the primary

+                // key columns have been added within the same session

+                if (super.areSupported(intermediateTable, changes))

+                {

+                    HashSet  addedColumns = new HashSet();

+                    String[] pkColNames   = null;

+

+                    for (Iterator it = changes.iterator(); it.hasNext();)

+                    {

+                        TableChange change = (TableChange)it.next();

+

+                        if (change instanceof AddColumnChange)

+                        {

+                            addedColumns.add(((AddColumnChange)change).getNewColumn().getName());

+                        }

+                        else if (change instanceof AddPrimaryKeyChange)

+                        {

+                            pkColNames = ((AddPrimaryKeyChange)change).getPrimaryKeyColumns();

+                        }

+                        else if (change instanceof PrimaryKeyChange)

+                        {

+                            pkColNames = ((PrimaryKeyChange)change).getNewPrimaryKeyColumns();

+                        }

+                    }

+                    if (pkColNames != null)

+                    {

+                        for (int colIdx = 0; colIdx < pkColNames.length; colIdx++)

+                        {

+                            if (addedColumns.contains(pkColNames[colIdx]))

+                            {

+                                return false;

+                            }

+                        }

+                    }

+                    return true;

+                }

+                else

+                {

+                    return false;

+                }

+            }

+

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                // Firebird cannot add columns to the primary key or drop columns from it but

+                // since we add/drop the primary key with separate changes anyways, this will

+                // no problem here

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof AddColumnChange))

+                {

+                    return true;

+                }

+                else

+                {

+                    return super.isSupported(intermediateTable, change);

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the addition of a column to 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,

+                              AddColumnChange    change) throws IOException

+    {

+        Table  changedTable = findChangedTable(currentModel, change);

+        Column prevColumn   = null;

+

+        if (change.getPreviousColumn() != null)

+        {

+            prevColumn = changedTable.findColumn(change.getPreviousColumn(), isDelimitedIdentifierModeOn());

+        }

+        ((FirebirdBuilder)getSqlBuilder()).insertColumn(currentModel,

+                                                        changedTable,

+                                                        change.getNewColumn(),

+                                                        prevColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column droppedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((FirebirdBuilder)getSqlBuilder()).dropColumn(changedTable, droppedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbBuilder.java b/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbBuilder.java
index cf97246..70471b5 100644
--- a/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbBuilder.java
@@ -20,17 +20,9 @@
  */
 
 import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.ListIterator;
-import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.TableChange;
-import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Table;
 import org.apache.ddlutils.platform.SqlBuilder;
 
@@ -72,108 +64,41 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // HsqlDb can only drop columns that are not part of a primary key
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if ((change instanceof RemoveColumnChange) && 
-                ((RemoveColumnChange)change).getChangedColumn().isPrimaryKey())
-            {
-                return;
-            }
-        }
-
-        // in order to utilize the ALTER TABLE ADD COLUMN BEFORE statement
-        // we have to apply the add column changes in the correct order
-        // thus we first gather all add column changes and then execute them
-        // Since we get them in target table column order, we can simply
-        // iterate backwards
-        ArrayList addColumnChanges = new ArrayList();
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                addColumnChanges.add(change);
-                changeIt.remove();
-            }
-        }
-        for (ListIterator changeIt = addColumnChanges.listIterator(addColumnChanges.size()); changeIt.hasPrevious();)
-        {
-            AddColumnChange addColumnChange = (AddColumnChange)changeIt.previous();
-
-            processChange(currentModel, desiredModel, addColumnChange);
-            changeIt.remove();
-        }
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemoveColumnChange) 
-            {
-                RemoveColumnChange removeColumnChange = (RemoveColumnChange)change;
-
-                processChange(currentModel, desiredModel, removeColumnChange);
-                changeIt.remove();
-            }
-        }
-    }
-
-    /**
-     * Processes the addition of a column to a table.
+     * Writes the SQL to add/insert a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table      The table
+     * @param newColumn  The new column
+     * @param nextColumn The column before which the new column shall be added; <code>null</code>
+     *                   if the new column is to be added instead of inserted
      */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void insertColumn(Table table, Column newColumn, Column nextColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD COLUMN ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
-        if (change.getNextColumn() != null)
+        writeColumn(table, newColumn);
+        if (nextColumn != null)
         {
             print(" BEFORE ");
-            printIdentifier(getColumnName(change.getNextColumn()));
+            printIdentifier(getColumnName(nextColumn));
         }
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbPlatform.java b/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbPlatform.java
index 787e664..73443f1 100644
--- a/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/hsqldb/HsqlDbPlatform.java
@@ -19,6 +19,7 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Connection;

 import java.sql.SQLException;

 import java.sql.Statement;

@@ -26,6 +27,16 @@
 

 import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -52,6 +63,7 @@
         info.setNonPKIdentityColumnsSupported(false);

         info.setIdentityOverrideAllowed(false);

         info.setSystemForeignKeyIndicesAlwaysNonUnique(true);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

 

         info.addNativeTypeMapping(Types.ARRAY,       "LONGVARBINARY", Types.LONGVARBINARY);

         info.addNativeTypeMapping(Types.BLOB,        "LONGVARBINARY", Types.LONGVARBINARY);

@@ -106,4 +118,85 @@
             closeStatement(stmt);    

         }

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if (change instanceof RemoveColumnChange)

+                {

+                    Column column = intermediateTable.findColumn(((RemoveColumnChange)change).getChangedColumn(),

+                                                                 isDelimitedIdentifierModeOn());

+

+                    // HsqlDb can only drop columns that are not part of a primary key

+                    return !column.isPrimaryKey();

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change; 

+

+                    // adding IDENTITY columns is not supported without a table rebuild because they have to

+                    // be PK columns, but we add them to the PK later

+                    return addColumnChange.isAtEnd() &&

+                           (!addColumnChange.getNewColumn().isRequired() ||

+                            (addColumnChange.getNewColumn().getDefaultValue() != null));

+                }

+                else if (change instanceof AddPrimaryKeyChange)

+                {

+                    return true;

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the addition of a column to 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,

+                              AddColumnChange    change) throws IOException

+    {

+        Table  changedTable = findChangedTable(currentModel, change);

+        Column nextColumn   = null;

+

+        if (change.getNextColumn() != null)

+        {

+            nextColumn = changedTable.findColumn(change.getNextColumn(), isDelimitedIdentifierModeOn());

+        }

+        ((HsqlDbBuilder)getSqlBuilder()).insertColumn(changedTable, change.getNewColumn(), nextColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((HsqlDbBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/interbase/InterbaseBuilder.java b/src/java/org/apache/ddlutils/platform/interbase/InterbaseBuilder.java
index c527607..8244c3b 100644
--- a/src/java/org/apache/ddlutils/platform/interbase/InterbaseBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/interbase/InterbaseBuilder.java
@@ -21,15 +21,9 @@
 

 import java.io.IOException;

 import java.sql.Types;

-import java.util.Iterator;

-import java.util.List;

 import java.util.Map;

 

 import org.apache.ddlutils.Platform;

-import org.apache.ddlutils.alteration.AddColumnChange;

-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

-import org.apache.ddlutils.alteration.RemoveColumnChange;

-import org.apache.ddlutils.alteration.TableChange;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Index;

@@ -105,7 +99,7 @@
     /**

      * {@inheritDoc}

      */

-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException

+    public void dropIndex(Table table, Index index) throws IOException

     {

         // Index names in Interbase are unique to a schema and hence we do not

         // need the ON <tablename> clause

@@ -219,133 +213,59 @@
     }

 

     /**

-     * {@inheritDoc}

-     */

-    protected void processTableStructureChanges(Database currentModel, Database desiredModel, Table sourceTable, Table targetTable, Map parameters, List changes) throws IOException

-    {

-        // TODO: Dropping of primary keys is currently not supported because we cannot

-        //       determine the pk constraint names and drop them in one go

-        //       (We could used a stored procedure if Interbase would allow them to use DDL)

-        //       This will be easier once named primary keys are supported

-        boolean pkColumnAdded = false;

-

-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)

-        {

-            TableChange change = (TableChange)changeIt.next();

-

-            if (change instanceof AddColumnChange)

-            {

-                AddColumnChange addColumnChange = (AddColumnChange)change;

-

-                // TODO: we cannot add columns to the primary key this way

-                //       because we would have to drop the pk first and then

-                //       add a new one afterwards which is not supported yet

-                if (addColumnChange.getNewColumn().isPrimaryKey())

-                {

-                    pkColumnAdded = true;   

-                }

-                else

-                {

-                    processChange(currentModel, desiredModel, addColumnChange);

-                    changeIt.remove();

-                }

-            }

-            else if (change instanceof RemoveColumnChange)

-            {

-                RemoveColumnChange removeColumnChange = (RemoveColumnChange)change;

-

-                // TODO: we cannot drop primary key columns this way

-                //       because we would have to drop the pk first and then

-                //       add a new one afterwards which is not supported yet

-                if (!removeColumnChange.getChangedColumn().isPrimaryKey())

-                {

-                    processChange(currentModel, desiredModel, removeColumnChange);

-                    changeIt.remove();

-                }

-            }

-        }

-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)

-        {

-            TableChange change = (TableChange)changeIt.next();

-

-            // we can only add a primary key if all columns are present in the table

-            // i.e. none was added during this alteration

-            if ((change instanceof AddPrimaryKeyChange) && !pkColumnAdded)

-            {

-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);

-                changeIt.remove();

-            }

-        }

-    }

-

-    /**

-     * Processes the addition of a column to a table.

+     * Writes the SQL to add/insert a column.

      * 

-     * @param currentModel The current database schema

-     * @param desiredModel The desired database schema

-     * @param change       The change object

+     * @param model      The database model

+     * @param table      The table

+     * @param newColumn  The new column

+     * @param prevColumn The column after which the new column shall be added; <code>null</code>

+     *                   if the new column is to be inserted at the beginning

      */

-    protected void processChange(Database        currentModel,

-                                 Database        desiredModel,

-                                 AddColumnChange change) throws IOException

+    public void insertColumn(Database model, Table table, Column newColumn, Column prevColumn) throws IOException

     {

         print("ALTER TABLE ");

-        printlnIdentifier(getTableName(change.getChangedTable()));

+        printlnIdentifier(getTableName(table));

         printIndent();

         print("ADD ");

-        writeColumn(change.getChangedTable(), change.getNewColumn());

+        writeColumn(table, newColumn);

         printEndOfStatement();

 

-        Table curTable = currentModel.findTable(change.getChangedTable().getName(), getPlatform().isDelimitedIdentifierModeOn());

-

-        if (!change.isAtEnd())

+        if (prevColumn != null)

         {

-            Column prevColumn = change.getPreviousColumn();

-

-            if (prevColumn != null)

-            {

-                // we need the corresponding column object from the current table

-                prevColumn = curTable.findColumn(prevColumn.getName(), getPlatform().isDelimitedIdentifierModeOn());

-            }

             // Even though Interbase can only add columns, we can move them later on

             print("ALTER TABLE ");

-            printlnIdentifier(getTableName(change.getChangedTable()));

+            printlnIdentifier(getTableName(table));

             printIndent();

             print("ALTER ");

-            printIdentifier(getColumnName(change.getNewColumn()));

+            printIdentifier(getColumnName(newColumn));

             print(" POSITION ");

             // column positions start at 1 in Interbase

-            print(prevColumn == null ? "1" : String.valueOf(curTable.getColumnIndex(prevColumn) + 2));

+            print(String.valueOf(table.getColumnIndex(prevColumn) + 2));

             printEndOfStatement();

         }

-        if (change.getNewColumn().isAutoIncrement())

+        if (newColumn.isAutoIncrement())

         {

-            writeAutoIncrementCreateStmts(currentModel, curTable, change.getNewColumn());

+            writeAutoIncrementCreateStmts(model, table, newColumn);

         }

-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());

     }

 

     /**

-     * Processes the removal of a column from a table.

+     * Writes the SQL to drop a column.

      * 

-     * @param currentModel The current database schema

-     * @param desiredModel The desired database schema

-     * @param change       The change object

+     * @param table  The table

+     * @param column The column to drop

      */

-    protected void processChange(Database           currentModel,

-                                 Database           desiredModel,

-                                 RemoveColumnChange change) throws IOException

+    public void dropColumn(Table table, Column column) throws IOException

     {

-        if (change.getChangedColumn().isAutoIncrement())

+        if (column.isAutoIncrement())

         {

-            writeAutoIncrementDropStmts(change.getChangedTable(), change.getChangedColumn());

+            writeAutoIncrementDropStmts(table, column);

         }

         print("ALTER TABLE ");

-        printlnIdentifier(getTableName(change.getChangedTable()));

+        printlnIdentifier(getTableName(table));

         printIndent();

         print("DROP ");

-        printIdentifier(getColumnName(change.getChangedColumn()));

+        printIdentifier(getColumnName(column));

         printEndOfStatement();

-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());

     }

 }

diff --git a/src/java/org/apache/ddlutils/platform/interbase/InterbasePlatform.java b/src/java/org/apache/ddlutils/platform/interbase/InterbasePlatform.java
index c0a74f8..ee9ecdb 100644
--- a/src/java/org/apache/ddlutils/platform/interbase/InterbasePlatform.java
+++ b/src/java/org/apache/ddlutils/platform/interbase/InterbasePlatform.java
@@ -27,9 +27,24 @@
 import java.sql.ResultSet;

 import java.sql.SQLException;

 import java.sql.Types;

+import java.util.HashSet;

+import java.util.Iterator;

+import java.util.List;

 

 import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -172,4 +187,130 @@
         }

     }

 

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        ModelComparator comparator = super.getModelComparator();

+

+        comparator.setCanDropPrimaryKeyColumns(false);

+        comparator.setGeneratePrimaryKeyChanges(false);

+        return comparator;

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            public boolean areSupported(Table intermediateTable, List changes)

+            {

+                // Firebird does support adding a primary key, but only if none of the primary

+                // key columns have been added within the same session

+                if (super.areSupported(intermediateTable, changes))

+                {

+                    HashSet  addedColumns = new HashSet();

+                    String[] pkColNames   = null;

+

+                    for (Iterator it = changes.iterator(); it.hasNext();)

+                    {

+                        TableChange change = (TableChange)it.next();

+

+                        if (change instanceof AddColumnChange)

+                        {

+                            addedColumns.add(((AddColumnChange)change).getNewColumn().getName());

+                        }

+                        else if (change instanceof AddPrimaryKeyChange)

+                        {

+                            pkColNames = ((AddPrimaryKeyChange)change).getPrimaryKeyColumns();

+                        }

+                        else if (change instanceof PrimaryKeyChange)

+                        {

+                            pkColNames = ((PrimaryKeyChange)change).getNewPrimaryKeyColumns();

+                        }

+                    }

+                    if (pkColNames != null)

+                    {

+                        for (int colIdx = 0; colIdx < pkColNames.length; colIdx++)

+                        {

+                            if (addedColumns.contains(pkColNames[colIdx]))

+                            {

+                                return false;

+                            }

+                        }

+                    }

+                    return true;

+                }

+                else

+                {

+                    return false;

+                }

+            }

+

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                // Firebird cannot add columns to the primary key or drop columns from it but

+                // since we add/drop the primary key with separate changes anyways, this will

+                // no problem here

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof AddColumnChange))

+                {

+                    return true;

+                }

+                else

+                {

+                    return super.isSupported(intermediateTable, change);

+                }

+            }

+        };

+    }

+

+

+    /**

+     * Processes the addition of a column to 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,

+                              AddColumnChange    change) throws IOException

+    {

+        Table  changedTable = findChangedTable(currentModel, change);

+        Column prevColumn   = null;

+

+        if (change.getPreviousColumn() != null)

+        {

+            prevColumn = changedTable.findColumn(change.getPreviousColumn(), isDelimitedIdentifierModeOn());

+        }

+        ((InterbaseBuilder)getSqlBuilder()).insertColumn(currentModel,

+                                                         changedTable,

+                                                         change.getNewColumn(),

+                                                         prevColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column droppedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((InterbaseBuilder)getSqlBuilder()).dropColumn(changedTable, droppedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/maxdb/MaxDbBuilder.java b/src/java/org/apache/ddlutils/platform/maxdb/MaxDbBuilder.java
index 4b0d565..b69fed5 100644
--- a/src/java/org/apache/ddlutils/platform/maxdb/MaxDbBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/maxdb/MaxDbBuilder.java
@@ -48,7 +48,7 @@
     /**

      * {@inheritDoc}

      */

-    protected void writeExternalPrimaryKeysCreateStmt(Table table, Column[] primaryKeyColumns) throws IOException

+    public void createPrimaryKey(Table table, Column[] primaryKeyColumns) throws IOException

     {

         if ((primaryKeyColumns.length > 0) && shouldGeneratePrimaryKeys(primaryKeyColumns))

         {

@@ -66,24 +66,24 @@
     /**

      * {@inheritDoc}

      */

-    protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ForeignKey key) throws IOException

+    public void createForeignKey(Database database, Table table, ForeignKey foreignKey) throws IOException

     {

-        if (key.getForeignTableName() == null)

+        if (foreignKey.getForeignTableName() == null)

         {

-            _log.warn("Foreign key table is null for key " + key);

+            _log.warn("Foreign key table is null for key " + foreignKey);

         }

         else

         {

             writeTableAlterStmt(table);

 

             print("ADD CONSTRAINT ");

-            printIdentifier(getForeignKeyName(table, key));

+            printIdentifier(getForeignKeyName(table, foreignKey));

             print(" FOREIGN KEY (");

-            writeLocalReferences(key);

+            writeLocalReferences(foreignKey);

             print(") REFERENCES ");

-            printIdentifier(getTableName(database.findTable(key.getForeignTableName())));

+            printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName())));

             print(" (");

-            writeForeignReferences(key);

+            writeForeignReferences(foreignKey);

             print(")");

             printEndOfStatement();

         }

@@ -92,7 +92,7 @@
     /**

      * {@inheritDoc}

      */

-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException

+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException

     {

         writeTableAlterStmt(table);

         print("DROP CONSTRAINT ");

diff --git a/src/java/org/apache/ddlutils/platform/mckoi/MckoiBuilder.java b/src/java/org/apache/ddlutils/platform/mckoi/MckoiBuilder.java
index 76466c8..efc7ad9 100644
--- a/src/java/org/apache/ddlutils/platform/mckoi/MckoiBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/mckoi/MckoiBuilder.java
@@ -20,15 +20,9 @@
  */
 
 import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.ColumnAutoIncrementChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Table;
@@ -172,77 +166,15 @@
     }
 
     /**
-     * {@inheritDoc}
+     * Writes the SQL to recreate a table.
+     * 
+     * @param model      The database model
+     * @param table      The table to recreate
+     * @param parameters The table creation parameters
      */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
+    protected void writeRecreateTableStmt(Database model, Table table, Map parameters) throws IOException
     {
-        // McKoi has this nice ALTER CREATE TABLE statement which saves us a lot of work
-        // We only have to handle auto-increment changes manually
-        for (Iterator it = changes.iterator(); it.hasNext();)
-        {
-            TableChange change = (TableChange)it.next();
-
-            if (change instanceof ColumnAutoIncrementChange)
-            {
-                Column column = ((ColumnAutoIncrementChange)change).getChangedColumn();
-
-                // we have to defer removal of the sequences until they are no longer used
-                if (!column.isAutoIncrement())
-                {
-                    ColumnAutoIncrementChange autoIncrChange = (ColumnAutoIncrementChange)change;
-
-                    createAutoIncrementSequence(autoIncrChange.getChangedTable(),
-                                                autoIncrChange.getChangedColumn());
-                }
-            }
-            else if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                if (addColumnChange.getNewColumn().isAutoIncrement())
-                {
-                    createAutoIncrementSequence(addColumnChange.getChangedTable(),
-                                                addColumnChange.getNewColumn());
-                }
-            }
-        }
-
         print("ALTER ");
-        super.createTable(desiredModel, targetTable, parameters);
-
-        for (Iterator it = changes.iterator(); it.hasNext();)
-        {
-            TableChange change = (TableChange)it.next();
-    
-            if (change instanceof ColumnAutoIncrementChange)
-            {
-                Column column = ((ColumnAutoIncrementChange)change).getChangedColumn();
-    
-                if (column.isAutoIncrement())
-                {
-                    ColumnAutoIncrementChange autoIncrChange = (ColumnAutoIncrementChange)change;
-        
-                    dropAutoIncrementSequence(autoIncrChange.getChangedTable(),
-                                              autoIncrChange.getChangedColumn());
-                }
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                RemoveColumnChange removeColumnChange = (RemoveColumnChange)change;
-
-                if (removeColumnChange.getChangedColumn().isAutoIncrement())
-                {
-                    dropAutoIncrementSequence(removeColumnChange.getChangedTable(),
-                                              removeColumnChange.getChangedColumn());
-                }
-            }
-        }
-        changes.clear();
+        super.createTable(model, table, parameters);
     }
 }
-
diff --git a/src/java/org/apache/ddlutils/platform/mckoi/MckoiPlatform.java b/src/java/org/apache/ddlutils/platform/mckoi/MckoiPlatform.java
index e09b638..6b6716e 100644
--- a/src/java/org/apache/ddlutils/platform/mckoi/MckoiPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/mckoi/MckoiPlatform.java
@@ -19,15 +19,28 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Connection;

 import java.sql.DriverManager;

 import java.sql.SQLException;

 import java.sql.Types;

 import java.util.Iterator;

+import java.util.List;

 import java.util.Map;

 

 import org.apache.ddlutils.DatabaseOperationException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.ColumnDefinitionChange;

+import org.apache.ddlutils.alteration.RecreateTableChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -147,7 +160,93 @@
         }

         else

         {

-            throw new UnsupportedOperationException("Unable to create a Derby database via the driver "+jdbcDriverClassName);

+            throw new UnsupportedOperationException("Unable to create a McKoi database via the driver "+jdbcDriverClassName);

+        }

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            public boolean areSupported(Table intermediateTable, List changes)

+            {

+                // McKoi has this nice ALTER CREATE TABLE statement which saves us a lot of work

+                // Thus, we reject all table level changes and instead redefine the handling of the

+                // RecreateTableChange

+                return false;

+            }

+        };

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    public void processChange(Database currentModel, CreationParameters params, RecreateTableChange change) throws IOException

+    {

+        // McKoi has this nice ALTER CREATE TABLE statement which saves us a lot of work

+        // We only have to handle auto-increment changes manually

+        MckoiBuilder sqlBuilder   = (MckoiBuilder)getSqlBuilder();

+        Table        changedTable = findChangedTable(currentModel, change);

+

+        for (Iterator it = change.getOriginalChanges().iterator(); it.hasNext();)

+        {

+            TableChange tableChange = (TableChange)it.next();

+

+            if (tableChange instanceof ColumnDefinitionChange)

+            {

+                ColumnDefinitionChange colChange  = (ColumnDefinitionChange)tableChange;

+                Column                 origColumn = changedTable.findColumn(colChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+                Column                 newColumn  = colChange.getNewColumn();

+

+                if (!origColumn.isAutoIncrement() && newColumn.isAutoIncrement())

+                {

+                    sqlBuilder.createAutoIncrementSequence(changedTable, origColumn);

+                }

+            }

+            else if (tableChange instanceof AddColumnChange)

+            {

+                AddColumnChange addColumnChange = (AddColumnChange)tableChange;

+

+                if (addColumnChange.getNewColumn().isAutoIncrement())

+                {

+                    sqlBuilder.createAutoIncrementSequence(changedTable, addColumnChange.getNewColumn());

+                }

+            }

+        }

+

+        Map parameters = (params == null ? null : params.getParametersFor(changedTable));

+        

+        sqlBuilder.writeRecreateTableStmt(currentModel, change.getTargetTable(), parameters);

+

+        // we have to defer removal of the sequences until they are no longer used

+        for (Iterator it = change.getOriginalChanges().iterator(); it.hasNext();)

+        {

+            TableChange tableChange = (TableChange)it.next();

+    

+            if (tableChange instanceof ColumnDefinitionChange)

+            {

+                ColumnDefinitionChange colChange  = (ColumnDefinitionChange)tableChange;

+                Column                 origColumn = changedTable.findColumn(colChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+                Column                 newColumn  = colChange.getNewColumn();

+

+                if (origColumn.isAutoIncrement() && !newColumn.isAutoIncrement())

+                {

+                    sqlBuilder.dropAutoIncrementSequence(changedTable, origColumn);

+                }

+            }

+            else if (tableChange instanceof RemoveColumnChange)

+            {

+                RemoveColumnChange removeColumnChange = (RemoveColumnChange)tableChange;

+                Column             removedColumn      = changedTable.findColumn(removeColumnChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+                if (removedColumn.isAutoIncrement())

+                {

+                    sqlBuilder.dropAutoIncrementSequence(changedTable, removedColumn);

+                }

+            }

         }

     }

 }

diff --git a/src/java/org/apache/ddlutils/platform/mssql/MSSqlBuilder.java b/src/java/org/apache/ddlutils/platform/mssql/MSSqlBuilder.java
index fd769c3..01bd8e5 100644
--- a/src/java/org/apache/ddlutils/platform/mssql/MSSqlBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/mssql/MSSqlBuilder.java
@@ -23,33 +23,15 @@
 import java.sql.Types;
 import java.text.DateFormat;
 import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.lang.StringUtils;
 import org.apache.ddlutils.Platform;
-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.ColumnAutoIncrementChange;
-import org.apache.ddlutils.alteration.ColumnChange;
-import org.apache.ddlutils.alteration.ColumnDataTypeChange;
-import org.apache.ddlutils.alteration.ColumnSizeChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-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.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
 import org.apache.ddlutils.model.Index;
 import org.apache.ddlutils.model.Table;
-import org.apache.ddlutils.platform.CreationParameters;
 import org.apache.ddlutils.platform.SqlBuilder;
 import org.apache.ddlutils.util.Jdbc3Utils;
 
@@ -81,7 +63,7 @@
      */
     public void createTable(Database database, Table table, Map parameters) throws IOException
     {
-        writeQuotationOnStatement();
+        turnOnQuotation();
         super.createTable(database, table, parameters);
     }
 
@@ -94,7 +76,7 @@
         String tableNameVar      = "tn" + createUniqueIdentifier();
         String constraintNameVar = "cn" + createUniqueIdentifier();
 
-        writeQuotationOnStatement();
+        turnOnQuotation();
         print("IF EXISTS (SELECT 1 FROM sysobjects WHERE type = 'U' AND name = ");
         printAlwaysSingleQuotedIdentifier(tableName);
         println(")");
@@ -123,10 +105,10 @@
     /**
      * {@inheritDoc}
      */
-    public void dropExternalForeignKeys(Table table) throws IOException
+    public void dropForeignKeys(Table table) throws IOException
     {
-        writeQuotationOnStatement();
-        super.dropExternalForeignKeys(table);
+        turnOnQuotation();
+        super.dropForeignKeys(table);
     }
 
     /**
@@ -227,7 +209,7 @@
     /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         print("DROP INDEX ");
         printIdentifier(getTableName(table));
@@ -239,7 +221,7 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException
+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException
     {
         String constraintName = getForeignKeyName(table, foreignKey);
 
@@ -272,9 +254,9 @@
     }
 
     /**
-     * Writes the statement that turns on the ability to write delimited identifiers.
+     * If quotation mode is on, then this writes the statement that turns on the ability to write delimited identifiers.
      */
-    private void writeQuotationOnStatement() throws IOException
+    protected void turnOnQuotation() throws IOException
     {
         print(getQuotationOnStatement());
     }
@@ -365,7 +347,7 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeCopyDataStatement(Table sourceTable, Table targetTable) throws IOException
+    protected void copyData(Table sourceTable, Table targetTable) throws IOException
     {
         // Sql Server per default does not allow us to insert values explicitly into
         // identity columns. However, we can change this behavior
@@ -378,7 +360,7 @@
             print(" ON");
             printEndOfStatement();
         }
-        super.writeCopyDataStatement(sourceTable, targetTable);
+        super.copyData(sourceTable, targetTable);
         // We have to turn it off ASAP because it can be on only for one table per session
         if (hasIdentityColumns)
         {
@@ -392,259 +374,110 @@
     /**
      * {@inheritDoc}
      */
-    protected void processChanges(Database currentModel, Database desiredModel, List changes, CreationParameters params) throws IOException
-    {
-        if (!changes.isEmpty())
-        {
-            writeQuotationOnStatement();
-        }
-        // For column data type and size changes, we need to drop and then re-create indexes
-        // and foreign keys using the column, as well as any primary keys containg
-        // these columns
-        // However, if the index/foreign key/primary key is already slated for removal or
-        // change, then we don't want to generate change duplication
-        HashSet removedIndexes     = new HashSet();
-        HashSet removedForeignKeys = new HashSet();
-        HashSet removedPKs         = new HashSet();
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            Object change = changeIt.next();
-
-            if (change instanceof RemoveIndexChange)
-            {
-                removedIndexes.add(((RemoveIndexChange)change).getChangedIndex());
-            }
-            else if (change instanceof RemoveForeignKeyChange)
-            {
-                removedForeignKeys.add(((RemoveForeignKeyChange)change).getChangedForeignKey());
-            }
-            else if (change instanceof RemovePrimaryKeyChange)
-            {
-                removedPKs.add(((RemovePrimaryKeyChange)change).getChangedTable());
-            }
-        }
-
-        ArrayList additionalChanges = new ArrayList();
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            Object change = changeIt.next();
-
-            if ((change instanceof ColumnDataTypeChange) ||
-                (change instanceof ColumnSizeChange))
-            {
-                Column column = ((ColumnChange)change).getChangedColumn();
-                Table  table  = ((ColumnChange)change).getChangedTable();
-
-                if (column.isPrimaryKey() && !removedPKs.contains(table))
-                {
-                    Column[] pk = table.getPrimaryKeyColumns();
-
-                    additionalChanges.add(new RemovePrimaryKeyChange(table, pk));
-                    additionalChanges.add(new AddPrimaryKeyChange(table, pk));
-                    removedPKs.add(table);
-                }
-                for (int idx = 0; idx < table.getIndexCount(); idx++)
-                {
-                    Index index = table.getIndex(idx);
-
-                    if (index.hasColumn(column) && !removedIndexes.contains(index))
-                    {
-                        additionalChanges.add(new RemoveIndexChange(table, index));
-                        additionalChanges.add(new AddIndexChange(table, index));
-                        removedIndexes.add(index);
-                    }
-                }
-                for (int tableIdx = 0; tableIdx < currentModel.getTableCount(); tableIdx++)
-                {
-                    Table curTable = currentModel.getTable(tableIdx);
-
-                    for (int fkIdx = 0; fkIdx < curTable.getForeignKeyCount(); fkIdx++)
-                    {
-                        ForeignKey curFk = curTable.getForeignKey(fkIdx);
-
-                        if ((curFk.hasLocalColumn(column) || curFk.hasForeignColumn(column)) &&
-                            !removedForeignKeys.contains(curFk))
-                        {
-                            additionalChanges.add(new RemoveForeignKeyChange(curTable, curFk));
-                            additionalChanges.add(new AddForeignKeyChange(curTable, curFk));
-                            removedForeignKeys.add(curFk);
-                        }
-                    }
-                }
-            }
-        }
-        changes.addAll(additionalChanges);
-        super.processChanges(currentModel, desiredModel, changes, params);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // First we drop primary keys as necessary
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange       pkChange       = (PrimaryKeyChange)change;
-                RemovePrimaryKeyChange removePkChange = new RemovePrimaryKeyChange(pkChange.getChangedTable(),
-                                                                                   pkChange.getOldPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, removePkChange);
-            }
-        }
-
-        ArrayList columnChanges = new ArrayList();
-
-        // Next we add/remove columns
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // Sql Server can only add not insert columns
-                if (addColumnChange.isAtEnd())
-                {
-                    processChange(currentModel, desiredModel, addColumnChange);
-                    changeIt.remove();
-                }
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof ColumnAutoIncrementChange)
-            {
-                // Sql Server has no way of adding or removing an IDENTITY constraint
-                // Thus we have to rebuild the table anyway and can ignore all the other 
-                // column changes
-                columnChanges = null;
-            }
-            else if ((change instanceof ColumnChange) && (columnChanges != null))
-            {
-                // we gather all changed columns because we can use the ALTER TABLE ALTER COLUMN
-                // statement for them
-                columnChanges.add(change);
-            }
-        }
-        if (columnChanges != null)
-        {
-            HashSet processedColumns = new HashSet();
-
-            for (Iterator changeIt = columnChanges.iterator(); changeIt.hasNext();)
-            {
-                ColumnChange change       = (ColumnChange)changeIt.next();
-                Column       sourceColumn = change.getChangedColumn();
-                Column       targetColumn = targetTable.findColumn(sourceColumn.getName(), getPlatform().isDelimitedIdentifierModeOn());
-
-                if (!processedColumns.contains(targetColumn))
-                {
-                    processColumnChange(sourceTable,
-                                        targetTable,
-                                        sourceColumn,
-                                        targetColumn,
-                                        (change instanceof ColumnDataTypeChange) || (change instanceof ColumnSizeChange));
-                    processedColumns.add(targetColumn);
-                }
-                changes.remove(change);
-                change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-            }
-        }
-        // Finally we add primary keys
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange    pkChange    = (PrimaryKeyChange)change;
-                AddPrimaryKeyChange addPkChange = new AddPrimaryKeyChange(pkChange.getChangedTable(),
-                                                                          pkChange.getNewPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, addPkChange);
-                changeIt.remove();
-            }
-        }
-    }
-
-    /**
-     * Processes the addition of a column to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void addColumn(Table table, Column newColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
+        writeColumn(table, newColumn);
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Generates the SQL to drop a column from a table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table where to drop the column from
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
+        if (!StringUtils.isEmpty(column.getDefaultValue()))
+        {
+            writeDropConstraintStatement(table, column, "D");
+        }
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a primary key from a table.
+     * Writes the SQL for dropping the primary key of the given table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table The table
      */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
+    public void dropPrimaryKey(Table table) throws IOException
     {
-        // TODO: this would be easier when named primary keys are supported
-        //       because then we can use ALTER TABLE DROP
-        String tableName         = getTableName(change.getChangedTable());
+        // this would be easier if named primary keys are supported
+        // because for named pks we could use ALTER TABLE DROP
+        writeDropConstraintStatement(table, null, "PK");
+    }
+
+    /**
+     * Writes the SQL to recreate a column, e.g. using a different type or similar.
+     * 
+     * @param table     The table
+     * @param curColumn The current column definition
+     * @param newColumn The new column definition
+     */
+    public void recreateColumn(Table table, Column curColumn, Column newColumn) throws IOException
+    {
+        boolean hasDefault       = curColumn.getParsedDefaultValue() != null;
+        boolean shallHaveDefault = newColumn.getParsedDefaultValue() != null;
+        String  newDefault       = newColumn.getDefaultValue();
+
+        // Sql Server does not like it if there is a default spec in the ALTER TABLE ALTER COLUMN
+        // statement; thus we have to change the default manually
+        if (newDefault != null)
+        {
+            newColumn.setDefaultValue(null);
+        }
+        if (hasDefault)
+        {
+            // we're dropping the old default
+            writeDropConstraintStatement(table, curColumn, "D");
+        }
+
+        print("ALTER TABLE ");
+        printlnIdentifier(getTableName(table));
+        printIndent();
+        print("ALTER COLUMN ");
+        writeColumn(table, newColumn);
+        printEndOfStatement();
+
+        if (shallHaveDefault)
+        {
+            newColumn.setDefaultValue(newDefault);
+
+            // if the column shall have a default, then we have to add it as a constraint
+            print("ALTER TABLE ");
+            printlnIdentifier(getTableName(table));
+            printIndent();
+            print("ADD CONSTRAINT ");
+            printIdentifier(getConstraintName("DF", table, curColumn.getName(), null));
+            writeColumnDefaultValueStmt(table, newColumn);
+            print(" FOR ");
+            printIdentifier(getColumnName(curColumn));
+            printEndOfStatement();
+        }
+    }
+
+    /**
+     * Writes the SQL to drop a constraint, e.g. a primary key or default value constraint.
+     * 
+     * @param table          The table that the constraint is on
+     * @param column         The column that the constraint is on; <code>null</code> for table-level
+     *                       constraints
+     * @param typeIdentifier The constraint type identifier as is specified for the
+     *                       <code>sysobjects</code> system table
+     */
+    protected void writeDropConstraintStatement(Table table, Column column, String typeIdentifier) throws IOException
+    {
+        String tableName         = getTableName(table);
+        String columnName        = column == null ? null : getColumnName(column);
         String tableNameVar      = "tn" + createUniqueIdentifier();
         String constraintNameVar = "cn" + createUniqueIdentifier();
 
@@ -653,7 +486,18 @@
         println("  DECLARE refcursor CURSOR FOR");
         println("  SELECT object_name(objs.parent_obj) tablename, objs.name constraintname");
         println("    FROM sysobjects objs JOIN sysconstraints cons ON objs.id = cons.constid");
-        print("    WHERE objs.xtype = 'PK' AND object_name(objs.parent_obj) = ");
+        print("    WHERE objs.xtype = '");
+        print(typeIdentifier);
+        println("' AND");
+        if (columnName != null)
+        {
+            print("          cons.colid = (SELECT colid FROM syscolumns WHERE id = object_id(");
+            printAlwaysSingleQuotedIdentifier(tableName);
+            print(") AND name = ");
+            printAlwaysSingleQuotedIdentifier(columnName);
+            println(") AND");
+        }
+        print("          object_name(objs.parent_obj) = ");
         printAlwaysSingleQuotedIdentifier(tableName);
         println("  OPEN refcursor");
         println("  FETCH NEXT FROM refcursor INTO @" + tableNameVar + ", @" + constraintNameVar);
@@ -666,89 +510,5 @@
         println("  DEALLOCATE refcursor");
         print("END");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes a change to a column.
-     * 
-     * @param sourceTable  The current table
-     * @param targetTable  The desired table
-     * @param sourceColumn The current column
-     * @param targetColumn The desired column
-     * @param typeChange   Whether this is a type change
-     */
-    protected void processColumnChange(Table   sourceTable,
-                                       Table   targetTable,
-                                       Column  sourceColumn,
-                                       Column  targetColumn,
-                                       boolean typeChange) throws IOException
-    {
-        boolean hasDefault       = sourceColumn.getParsedDefaultValue() != null;
-        boolean shallHaveDefault = targetColumn.getParsedDefaultValue() != null;
-        String  newDefault       = targetColumn.getDefaultValue();
-
-        // Sql Server does not like it if there is a default spec in the ALTER TABLE ALTER COLUMN
-        // statement; thus we have to change the default manually
-        if (newDefault != null)
-        {
-            targetColumn.setDefaultValue(null);
-        }
-        if (hasDefault)
-        {
-            // we're dropping the old default
-            String tableName         = getTableName(sourceTable);
-            String columnName        = getColumnName(sourceColumn);
-            String tableNameVar      = "tn" + createUniqueIdentifier();
-            String constraintNameVar = "cn" + createUniqueIdentifier();
-
-            println("BEGIN");
-            println("  DECLARE @" + tableNameVar + " nvarchar(256), @" + constraintNameVar + " nvarchar(256)");
-            println("  DECLARE refcursor CURSOR FOR");
-            println("  SELECT object_name(objs.parent_obj) tablename, objs.name constraintname");
-            println("    FROM sysobjects objs JOIN sysconstraints cons ON objs.id = cons.constid");
-            println("    WHERE objs.xtype = 'D' AND");
-            print("          cons.colid = (SELECT colid FROM syscolumns WHERE id = object_id(");
-            printAlwaysSingleQuotedIdentifier(tableName);
-            print(") AND name = ");
-            printAlwaysSingleQuotedIdentifier(columnName);
-            println(") AND");
-            print("          object_name(objs.parent_obj) = ");
-            printAlwaysSingleQuotedIdentifier(tableName);
-            println("  OPEN refcursor");
-            println("  FETCH NEXT FROM refcursor INTO @" + tableNameVar + ", @" + constraintNameVar);
-            println("  WHILE @@FETCH_STATUS = 0");
-            println("    BEGIN");
-            println("      EXEC ('ALTER TABLE '+@" + tableNameVar + "+' DROP CONSTRAINT '+@" + constraintNameVar + ")");
-            println("      FETCH NEXT FROM refcursor INTO @" + tableNameVar + ", @" + constraintNameVar);
-            println("    END");
-            println("  CLOSE refcursor");
-            println("  DEALLOCATE refcursor");
-            print("END");
-            printEndOfStatement();
-        }
-
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(sourceTable));
-        printIndent();
-        print("ALTER COLUMN ");
-        writeColumn(sourceTable, targetColumn);
-        printEndOfStatement();
-
-        if (shallHaveDefault)
-        {
-            targetColumn.setDefaultValue(newDefault);
-
-            // if the column shall have a default, then we have to add it as a constraint
-            print("ALTER TABLE ");
-            printlnIdentifier(getTableName(sourceTable));
-            printIndent();
-            print("ADD CONSTRAINT ");
-            printIdentifier(getConstraintName("DF", sourceTable, sourceColumn.getName(), null));
-            writeColumnDefaultValueStmt(sourceTable, targetColumn);
-            print(" FOR ");
-            printIdentifier(getColumnName(sourceColumn));
-            printEndOfStatement();
-        }
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/mssql/MSSqlModelComparator.java b/src/java/org/apache/ddlutils/platform/mssql/MSSqlModelComparator.java
new file mode 100644
index 0000000..4a6c113
--- /dev/null
+++ b/src/java/org/apache/ddlutils/platform/mssql/MSSqlModelComparator.java
@@ -0,0 +1,325 @@
+package org.apache.ddlutils.platform.mssql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ddlutils.PlatformInfo;
+import org.apache.ddlutils.alteration.AddForeignKeyChange;
+import org.apache.ddlutils.alteration.AddIndexChange;
+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
+import org.apache.ddlutils.alteration.ModelComparator;
+import org.apache.ddlutils.alteration.RemoveForeignKeyChange;
+import org.apache.ddlutils.alteration.RemoveIndexChange;
+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;
+import org.apache.ddlutils.model.Column;
+import org.apache.ddlutils.model.Database;
+import org.apache.ddlutils.model.ForeignKey;
+import org.apache.ddlutils.model.Index;
+import org.apache.ddlutils.model.Table;
+import org.apache.ddlutils.util.StringUtils;
+
+/**
+ * A model comparator customized for Sql Server.
+ * 
+ * @version $Revision: $
+ */
+public class MSSqlModelComparator extends ModelComparator
+{
+    /**
+     * Creates a new Sql Server model comparator object.
+     * 
+     * @param platformInfo            The platform info
+     * @param tableDefChangePredicate The predicate that defines whether tables changes are supported
+     *                                by the platform or not; all changes are supported if this is null
+     * @param caseSensitive           Whether comparison is case sensitive
+     */
+    public MSSqlModelComparator(PlatformInfo                    platformInfo,
+                                TableDefinitionChangesPredicate tableDefChangePredicate,
+                                boolean                         caseSensitive)
+    {
+        super(platformInfo, tableDefChangePredicate, caseSensitive);
+        setGeneratePrimaryKeyChanges(false);
+        setCanDropPrimaryKeyColumns(false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForPrimaryKeyChanges(Database sourceModel,
+                                             Table    sourceTable,
+                                             Database intermediateModel,
+                                             Table    intermediateTable,
+                                             Database targetModel,
+                                             Table    targetTable)
+    {
+        List changes = super.checkForPrimaryKeyChanges(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable);
+
+        // now we add pk changes if one of the pk columns was changed
+        // we only need to do this if there is no other pk change (which can only be a remove or add change or both)
+        if (changes.isEmpty())
+        {
+            List columns = getRelevantChangedColumns(sourceTable, targetTable);
+
+            for (Iterator it = columns.iterator(); it.hasNext();)
+            {
+                Column targetColumn = (Column)it.next();
+
+                if (targetColumn.isPrimaryKey())
+                {
+                    changes.add(new RemovePrimaryKeyChange(sourceTable.getName()));
+                    changes.add(new AddPrimaryKeyChange(sourceTable.getName(), sourceTable.getPrimaryKeyColumnNames()));
+                    break;
+                }
+            }
+        }
+        return changes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForRemovedIndexes(Database sourceModel,
+                                          Table    sourceTable,
+                                          Database intermediateModel,
+                                          Table    intermediateTable,
+                                          Database targetModel,
+                                          Table    targetTable)
+    {
+        List    changes           = super.checkForRemovedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable);
+        Index[] targetIndexes     = targetTable.getIndices();
+        List    additionalChanges = new ArrayList();
+
+        // removing all indexes that are maintained and that use a changed column
+        if (targetIndexes.length > 0)
+        {
+            List columns = getRelevantChangedColumns(sourceTable, targetTable);
+
+            if (!columns.isEmpty())
+            {
+                for (int indexIdx = 0; indexIdx < targetIndexes.length; indexIdx++)
+                {
+                    Index sourceIndex = findCorrespondingIndex(sourceTable, targetIndexes[indexIdx]);
+
+                    if (sourceIndex != null)
+                    {
+                        for (Iterator columnIt = columns.iterator(); columnIt.hasNext();)
+                        {
+                            Column targetColumn = (Column)columnIt.next();
+
+                            if (targetIndexes[indexIdx].hasColumn(targetColumn))
+                            {
+                                additionalChanges.add(new RemoveIndexChange(intermediateTable.getName(), targetIndexes[indexIdx]));
+                                break;
+                            }
+                        }
+                    }
+                }
+                for (Iterator changeIt = additionalChanges.iterator(); changeIt.hasNext();)
+                {
+                    ((RemoveIndexChange)changeIt.next()).apply(intermediateModel, isCaseSensitive());
+                }
+                changes.addAll(additionalChanges);
+            }
+        }
+        return changes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForAddedIndexes(Database sourceModel,
+                                        Table    sourceTable,
+                                        Database intermediateModel,
+                                        Table    intermediateTable,
+                                        Database targetModel,
+                                        Table    targetTable)
+    {
+        List    changes           = super.checkForAddedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable);
+        Index[] targetIndexes     = targetTable.getIndices();
+        List    additionalChanges = new ArrayList();
+
+        // re-adding all indexes that are maintained and that use a changed column
+        if (targetIndexes.length > 0)
+        {
+            for (int indexIdx = 0; indexIdx < targetIndexes.length; indexIdx++)
+            {
+                Index sourceIndex       = findCorrespondingIndex(sourceTable, targetIndexes[indexIdx]);
+                Index intermediateIndex = findCorrespondingIndex(intermediateTable, targetIndexes[indexIdx]);
+
+                if ((sourceIndex != null) && (intermediateIndex == null))
+                {
+                    additionalChanges.add(new AddIndexChange(intermediateTable.getName(), targetIndexes[indexIdx]));
+                }
+            }
+            for (Iterator changeIt = additionalChanges.iterator(); changeIt.hasNext();)
+            {
+                ((AddIndexChange)changeIt.next()).apply(intermediateModel, isCaseSensitive());
+            }
+            changes.addAll(additionalChanges);
+        }
+        return changes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForRemovedForeignKeys(Database sourceModel,
+                                              Database intermediateModel,
+                                              Database targetModel)
+    {
+        List changes           = super.checkForRemovedForeignKeys(sourceModel, intermediateModel, targetModel);
+        List additionalChanges = new ArrayList();
+
+        // removing all foreign keys that are maintained and that use a changed column
+        for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++)
+        {
+            Table targetTable = targetModel.getTable(tableIdx);
+            Table sourceTable = sourceModel.findTable(targetTable.getName(), isCaseSensitive());
+
+            if (sourceTable != null)
+            {
+                List columns = getRelevantChangedColumns(sourceTable, targetTable);
+
+                if (!columns.isEmpty())
+                {
+                    for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)
+                    {
+                        ForeignKey targetFk = targetTable.getForeignKey(fkIdx);
+                        ForeignKey sourceFk = findCorrespondingForeignKey(sourceTable, targetFk);
+
+                        if (sourceFk != null)
+                        {
+                            for (Iterator columnIt = columns.iterator(); columnIt.hasNext();)
+                            {
+                                Column targetColumn = (Column)columnIt.next();
+
+                                if (targetFk.hasLocalColumn(targetColumn) || targetFk.hasForeignColumn(targetColumn))
+                                {
+                                    additionalChanges.add(new RemoveForeignKeyChange(sourceTable.getName(), targetFk));
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        for (Iterator changeIt = additionalChanges.iterator(); changeIt.hasNext();)
+        {
+            ((RemoveForeignKeyChange)changeIt.next()).apply(intermediateModel, isCaseSensitive());
+        }
+        changes.addAll(additionalChanges);
+        return changes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForAddedForeignKeys(Database sourceModel,
+                                            Database intermediateModel,
+                                            Database targetModel)
+    {
+        List changes           = super.checkForAddedForeignKeys(sourceModel, intermediateModel, targetModel);
+        List additionalChanges = new ArrayList();
+
+        // re-adding all foreign keys that are maintained and that use a changed column
+        for (int tableIdx = 0; tableIdx < targetModel.getTableCount(); tableIdx++)
+        {
+            Table targetTable       = targetModel.getTable(tableIdx);
+            Table sourceTable       = sourceModel.findTable(targetTable.getName(), isCaseSensitive());
+            Table intermediateTable = intermediateModel.findTable(targetTable.getName(), isCaseSensitive());
+
+            if (sourceTable != null)
+            {
+                for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)
+                {
+                    ForeignKey targetFk       = targetTable.getForeignKey(fkIdx);
+                    ForeignKey sourceFk       = findCorrespondingForeignKey(sourceTable, targetFk);
+                    ForeignKey intermediateFk = findCorrespondingForeignKey(intermediateTable, targetFk);
+
+                    if ((sourceFk != null) && (intermediateFk == null))
+                    {
+                        additionalChanges.add(new AddForeignKeyChange(intermediateTable.getName(), targetFk));
+                    }
+                }
+            }
+        }
+        for (Iterator changeIt = additionalChanges.iterator(); changeIt.hasNext();)
+        {
+            ((AddForeignKeyChange)changeIt.next()).apply(intermediateModel, isCaseSensitive());
+        }
+        changes.addAll(additionalChanges);
+        return changes;
+    }
+
+    /**
+     * Returns all columns that are changed in a way that makes it necessary to recreate foreign keys and
+     * indexes using them.
+     *  
+     * @param sourceTable The source table
+     * @param targetTable The target table
+     * @return The columns (from the target table)
+     */
+    private List getRelevantChangedColumns(Table sourceTable, Table targetTable)
+    {
+        List result = new ArrayList();
+
+        for (int columnIdx = 0; columnIdx < targetTable.getColumnCount(); columnIdx++)
+        {
+            Column targetColumn = targetTable.getColumn(columnIdx);
+            Column sourceColumn = sourceTable.findColumn(targetColumn.getName(), isCaseSensitive());
+
+            if (sourceColumn != null)
+            {
+                boolean hasChange      = false;
+                int     targetTypeCode = getPlatformInfo().getTargetJdbcType(targetColumn.getTypeCode());
+                boolean sizeMatters    = getPlatformInfo().hasSize(targetTypeCode);
+                boolean scaleMatters   = getPlatformInfo().hasPrecisionAndScale(targetTypeCode);
+
+                if (targetTypeCode != sourceColumn.getTypeCode())
+                {
+                    hasChange = true;
+                }
+                else
+                {
+                    if (sizeMatters && !StringUtils.equals(sourceColumn.getSize(), targetColumn.getSize()))
+                    {
+                        hasChange = true;
+                    }
+                    else if (scaleMatters &&
+                             (!StringUtils.equals(sourceColumn.getSize(), targetColumn.getSize()) ||
+                              (sourceColumn.getScale() != targetColumn.getScale())))
+                    {
+                        hasChange = true;
+                    }
+                }
+                if (hasChange)
+                {
+                    result.add(targetColumn);
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/src/java/org/apache/ddlutils/platform/mssql/MSSqlPlatform.java b/src/java/org/apache/ddlutils/platform/mssql/MSSqlPlatform.java
index 8ec0349..9bc27ef 100644
--- a/src/java/org/apache/ddlutils/platform/mssql/MSSqlPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/mssql/MSSqlPlatform.java
@@ -19,12 +19,29 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Connection;

 import java.sql.SQLException;

 import java.sql.Types;

+import java.util.Collection;

 

+import org.apache.commons.lang.StringUtils;

+import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ColumnDefinitionChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -55,6 +72,8 @@
         PlatformInfo info = getPlatformInfo();

 

         info.setMaxIdentifierLength(128);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

+        info.setIdentityColumnAutomaticallyRequired(true);

 

         info.addNativeTypeMapping(Types.ARRAY,         "IMAGE",         Types.LONGVARBINARY);

         // BIGINT will be mapped back to BIGINT by the model reader 

@@ -149,4 +168,147 @@
     {

         afterInsert(connection, table);

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        return new MSSqlModelComparator(getPlatformInfo(), getTableDefinitionChangesPredicate(), isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof AddPrimaryKeyChange) ||

+                    (change instanceof PrimaryKeyChange) ||

+                    (change instanceof RemovePrimaryKeyChange))

+                {

+                    return true;

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // Sql Server can only add not insert columns, and the cannot be requird unless also

+                    // auto increment or with a DEFAULT value

+                    return (addColumnChange.getNextColumn() == null) &&

+                           (!addColumnChange.getNewColumn().isRequired() ||

+                            addColumnChange.getNewColumn().isAutoIncrement() ||

+                            !StringUtils.isEmpty(addColumnChange.getNewColumn().getDefaultValue()));

+                }

+                else if (change instanceof ColumnDefinitionChange)

+                {

+                    ColumnDefinitionChange colDefChange = (ColumnDefinitionChange)change;

+                    Column                 curColumn    = intermediateTable.findColumn(colDefChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+                    // Sql Server has no way of adding or removing an IDENTITY constraint

+                    return curColumn.isAutoIncrement() == colDefChange.getNewColumn().isAutoIncrement();

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected Database processChanges(Database model, Collection changes, CreationParameters params) throws IOException, DdlUtilsException

+    {

+        if (!changes.isEmpty())

+        {

+            ((MSSqlBuilder)getSqlBuilder()).turnOnQuotation();

+        }

+        return super.processChanges(model, changes, params);

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((MSSqlBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((MSSqlBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the column 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,

+                              ColumnDefinitionChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column changedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((MSSqlBuilder)getSqlBuilder()).recreateColumn(changedTable, changedColumn, change.getNewColumn());

+    }

+    

+    /**

+     * Processes the change of the primary key 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,

+                              PrimaryKeyChange   change) throws IOException

+    {

+        Table    changedTable     = findChangedTable(currentModel, change);

+        String[] newPKColumnNames = change.getNewPrimaryKeyColumns();

+        Column[] newPKColumns     = new Column[newPKColumnNames.length];

+

+        for (int colIdx = 0; colIdx < newPKColumnNames.length; colIdx++)

+        {

+            newPKColumns[colIdx] = changedTable.findColumn(newPKColumnNames[colIdx], isDelimitedIdentifierModeOn());

+        }

+        ((MSSqlBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        getSqlBuilder().createPrimaryKey(changedTable, newPKColumns);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/mysql/MySqlBuilder.java b/src/java/org/apache/ddlutils/platform/mysql/MySqlBuilder.java
index f62d15f..eb1a5a5 100644
--- a/src/java/org/apache/ddlutils/platform/mysql/MySqlBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/mysql/MySqlBuilder.java
@@ -20,22 +20,11 @@
  */
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
-import org.apache.commons.collections.set.ListOrderedSet;
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
-import org.apache.ddlutils.alteration.ColumnChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
-import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
 import org.apache.ddlutils.model.Table;
 import org.apache.ddlutils.platform.SqlBuilder;
@@ -146,13 +135,14 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException
+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException
     {
         writeTableAlterStmt(table);
         print("DROP FOREIGN KEY ");
         printIdentifier(getForeignKeyName(table, foreignKey));
         printEndOfStatement();
 
+        // InnoDB won't drop the auto-index for the foreign key automatically, so we have to do it
         if (foreignKey.isAutoIndexPresent())
         {
             writeTableAlterStmt(table);
@@ -160,192 +150,78 @@
             printIdentifier(getForeignKeyName(table, foreignKey));
             printEndOfStatement();
         }
-    }    
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // in order to utilize the ALTER TABLE ADD COLUMN AFTER statement
-        // we have to apply the add column changes in the correct order
-        // thus we first gather all add column changes and then execute them
-        ArrayList addColumnChanges = new ArrayList();
-
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                addColumnChanges.add((AddColumnChange)change);
-                changeIt.remove();
-            }
-        }
-        for (Iterator changeIt = addColumnChanges.iterator(); changeIt.hasNext();)
-        {
-            AddColumnChange addColumnChange = (AddColumnChange)changeIt.next();
-
-            processChange(currentModel, desiredModel, addColumnChange);
-            changeIt.remove();
-        }
-
-        ListOrderedSet changedColumns = new ListOrderedSet();
-        
-        // we don't have to care about the order because the comparator will have ensured
-        // that a add primary key change comes after all necessary columns are present
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (PrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof ColumnChange)
-            {
-                // we gather all changed columns because we can use the ALTER TABLE MODIFY COLUMN
-                // statement for them
-                changedColumns.add(((ColumnChange)change).getChangedColumn());
-                changeIt.remove();
-            }
-        }
-        for (Iterator columnIt = changedColumns.iterator(); columnIt.hasNext();)
-        {
-            Column sourceColumn = (Column)columnIt.next();
-            Column targetColumn = targetTable.findColumn(sourceColumn.getName(), getPlatform().isDelimitedIdentifierModeOn());
-
-            processColumnChange(sourceTable, targetTable, sourceColumn, targetColumn);
-        }
     }
 
     /**
-     * Processes the addition of a column to a table.
+     * Writes the SQL to add/insert a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table      The table
+     * @param newColumn  The new column
+     * @param prevColumn The column after which the new column shall be added; <code>null</code>
+     *                   if the new column is to be inserted at the beginning
      */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void insertColumn(Table table, Column newColumn, Column prevColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD COLUMN ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
-        if (change.getPreviousColumn() != null)
+        writeColumn(table, newColumn);
+        if (prevColumn != null)
         {
             print(" AFTER ");
-            printIdentifier(getColumnName(change.getPreviousColumn()));
+            printIdentifier(getColumnName(prevColumn));
         }
         else
         {
             print(" FIRST");
         }
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a primary key from a table.
+     * Writes the SQL to drop the primary key of the given table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table The table
      */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
+    public void dropPrimaryKey(Table table) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP PRIMARY KEY");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the change of the primary key of a table.
+     * Writes the SQL to recreate a column, e.g. with a different type.  
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The new column definition
      */
-    protected void processChange(Database         currentModel,
-                                 Database         desiredModel,
-                                 PrimaryKeyChange change) throws IOException
+    public void recreateColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("DROP PRIMARY KEY");
-        printEndOfStatement();
-        writeExternalPrimaryKeysCreateStmt(change.getChangedTable(), change.getNewPrimaryKeyColumns());
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes a change to a column.
-     * 
-     * @param sourceTable  The current table
-     * @param targetTable  The desired table
-     * @param sourceColumn The current column
-     * @param targetColumn The desired column
-     */
-    protected void processColumnChange(Table  sourceTable,
-                                       Table  targetTable,
-                                       Column sourceColumn,
-                                       Column targetColumn) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(sourceTable));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("MODIFY COLUMN ");
-        writeColumn(targetTable, targetColumn);
+        writeColumn(table, column);
         printEndOfStatement();
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/mysql/MySqlModelComparator.java b/src/java/org/apache/ddlutils/platform/mysql/MySqlModelComparator.java
new file mode 100644
index 0000000..55bede4
--- /dev/null
+++ b/src/java/org/apache/ddlutils/platform/mysql/MySqlModelComparator.java
@@ -0,0 +1,171 @@
+package org.apache.ddlutils.platform.mysql;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ddlutils.PlatformInfo;
+import org.apache.ddlutils.alteration.AddForeignKeyChange;
+import org.apache.ddlutils.alteration.ColumnDefinitionChange;
+import org.apache.ddlutils.alteration.ModelComparator;
+import org.apache.ddlutils.alteration.RemoveForeignKeyChange;
+import org.apache.ddlutils.alteration.RemoveIndexChange;
+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;
+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.Reference;
+import org.apache.ddlutils.model.Table;
+import org.apache.ddlutils.util.StringUtils;
+
+/**
+ * A model comparator customized for MySql.
+ * 
+ * @version $Revision: $
+ */
+public class MySqlModelComparator extends ModelComparator
+{
+    /**
+     * Creates a new MySql model comparator object.
+     * 
+     * @param platformInfo            The platform info
+     * @param tableDefChangePredicate The predicate that defines whether tables changes are supported
+     *                                by the platform or not; all changes are supported if this is null
+     * @param caseSensitive           Whether comparison is case sensitive
+     */
+    public MySqlModelComparator(PlatformInfo                    platformInfo,
+                                TableDefinitionChangesPredicate tableDefChangePredicate,
+                                boolean                         caseSensitive)
+    {
+        super(platformInfo, tableDefChangePredicate, caseSensitive);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List checkForRemovedIndexes(Database sourceModel,
+                                          Table    sourceTable,
+                                          Database intermediateModel,
+                                          Table    intermediateTable,
+                                          Database targetModel,
+                                          Table    targetTable)
+    {
+        // Handling for http://bugs.mysql.com/bug.php?id=21395: we need to drop and then recreate FKs that reference columns
+        // included in indexes that will be dropped
+        List changes     = super.checkForRemovedIndexes(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable);
+        Set  columnNames = new HashSet();
+
+        for (Iterator it = changes.iterator(); it.hasNext();)
+        {
+            RemoveIndexChange change = (RemoveIndexChange)it.next();
+            Index             index  = change.findChangedIndex(sourceModel, isCaseSensitive());
+
+            for (int colIdx = 0; colIdx < index.getColumnCount(); colIdx++)
+            {
+                columnNames.add(index.getColumn(colIdx).getName());
+            }
+        }
+        if (!columnNames.isEmpty())
+        {
+            // this is only relevant if the columns are referenced by foreign keys in the same table
+            changes.addAll(getForeignKeyRecreationChanges(intermediateTable, targetTable, columnNames));
+        }
+        return changes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected List compareTables(Database sourceModel,
+                                 Table    sourceTable,
+                                 Database intermediateModel,
+                                 Table    intermediateTable,
+                                 Database targetModel, Table targetTable)
+    {
+        // we need to drop and recreate foreign keys that reference columns whose data type will be changed (but not size)
+        List changes     = super.compareTables(sourceModel, sourceTable, intermediateModel, intermediateTable, targetModel, targetTable);
+        Set  columnNames = new HashSet();
+
+        for (Iterator it = changes.iterator(); it.hasNext();)
+        {
+            Object change = it.next();
+
+            if (change instanceof ColumnDefinitionChange)
+            {
+                ColumnDefinitionChange colDefChange = (ColumnDefinitionChange)change;
+                Column                 sourceColumn = sourceTable.findColumn(colDefChange.getChangedColumn(), isCaseSensitive());
+
+                if (ColumnDefinitionChange.isTypeChanged(getPlatformInfo(), sourceColumn, colDefChange.getNewColumn()))
+                {
+                    columnNames.add(sourceColumn.getName());
+                }
+            }
+        }
+        if (!columnNames.isEmpty())
+        {
+            // we don't need to check for foreign columns as the data type of both local and foreign column need
+            // to be changed, otherwise MySql will complain
+            changes.addAll(getForeignKeyRecreationChanges(intermediateTable, targetTable, columnNames));
+        }
+        return changes;
+    }
+
+    /**
+     * Returns remove and add changes for the foreign keys that reference the indicated columns as a local column.
+     * 
+     * @param intermediateTable The intermediate table
+     * @param targetTable       The target table
+     * @param columnNames       The names of the columns to look for
+     * @return The additional changes
+     */
+    private List getForeignKeyRecreationChanges(Table intermediateTable, Table targetTable, Set columnNames)
+    {
+        List newChanges = new ArrayList();
+
+        for (int fkIdx = 0; fkIdx < targetTable.getForeignKeyCount(); fkIdx++)
+        {
+            ForeignKey targetFk       = targetTable.getForeignKey(fkIdx);
+            ForeignKey intermediateFk = intermediateTable.findForeignKey(targetFk, isCaseSensitive());
+
+            if (intermediateFk != null)
+            {
+                for (int refIdx = 0; refIdx < intermediateFk.getReferenceCount(); refIdx++)
+                {
+                    Reference ref = intermediateFk.getReference(refIdx);
+
+                    for (Iterator colNameIt = columnNames.iterator(); colNameIt.hasNext();)
+                    {
+                        if (StringUtils.equals(ref.getLocalColumnName(), (String)colNameIt.next(), isCaseSensitive()))
+                        {
+                            newChanges.add(new RemoveForeignKeyChange(intermediateTable.getName(), intermediateFk));
+                            newChanges.add(new AddForeignKeyChange(intermediateTable.getName(), intermediateFk));
+                        }
+                    }
+                }
+            }
+        }
+        return newChanges;
+    }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ddlutils/platform/mysql/MySqlPlatform.java b/src/java/org/apache/ddlutils/platform/mysql/MySqlPlatform.java
index 3ca0134..8773f9e 100644
--- a/src/java/org/apache/ddlutils/platform/mysql/MySqlPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/mysql/MySqlPlatform.java
@@ -19,9 +19,24 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Types;

 

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ColumnDefinitionChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -54,6 +69,7 @@
         info.setNonPKIdentityColumnsSupported(false);

         // MySql returns synthetic default values for pk columns

         info.setSyntheticDefaultValueForRequiredReturned(true);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

         info.setCommentPrefix("#");

         // Double quotes are only allowed for delimiting identifiers if the server SQL mode includes ANSI_QUOTES 

         info.setDelimiterToken("`");

@@ -98,4 +114,142 @@
     {

         return DATABASENAME;

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        return new MySqlModelComparator(getPlatformInfo(), getTableDefinitionChangesPredicate(), isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    return !addColumnChange.getNewColumn().isAutoIncrement();

+                }

+                else

+                {

+                    return (change instanceof ColumnDefinitionChange) ||

+                           (change instanceof RemoveColumnChange) ||

+                           (change instanceof AddPrimaryKeyChange) ||

+                           (change instanceof PrimaryKeyChange) ||

+                           (change instanceof RemovePrimaryKeyChange);

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the addition of a column to 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,

+                              AddColumnChange    change) throws IOException

+    {

+        Table  changedTable = findChangedTable(currentModel, change);

+        Column prevColumn   = null;

+

+        if (change.getPreviousColumn() != null)

+        {

+            prevColumn = changedTable.findColumn(change.getPreviousColumn(), isDelimitedIdentifierModeOn());

+        }

+        ((MySqlBuilder)getSqlBuilder()).insertColumn(changedTable, change.getNewColumn(), prevColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the column 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,

+                              ColumnDefinitionChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((MySqlBuilder)getSqlBuilder()).recreateColumn(changedTable, change.getNewColumn());

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((MySqlBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((MySqlBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the primary key 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,

+                              PrimaryKeyChange   change) throws IOException

+    {

+        Table    changedTable     = findChangedTable(currentModel, change);

+        String[] newPKColumnNames = change.getNewPrimaryKeyColumns();

+        Column[] newPKColumns     = new Column[newPKColumnNames.length];

+

+        for (int colIdx = 0; colIdx < newPKColumnNames.length; colIdx++)

+        {

+            newPKColumns[colIdx] = changedTable.findColumn(newPKColumnNames[colIdx], isDelimitedIdentifierModeOn());

+        }

+        

+        ((MySqlBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        getSqlBuilder().createPrimaryKey(changedTable, newPKColumns);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/oracle/Oracle8Builder.java b/src/java/org/apache/ddlutils/platform/oracle/Oracle8Builder.java
index 98aa96e..b1d4f76 100644
--- a/src/java/org/apache/ddlutils/platform/oracle/Oracle8Builder.java
+++ b/src/java/org/apache/ddlutils/platform/oracle/Oracle8Builder.java
@@ -21,18 +21,10 @@
 
 import java.io.IOException;
 import java.sql.Types;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.ddlutils.DdlUtilsException;
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Index;
@@ -246,7 +238,7 @@
     /**
      * {@inheritDoc}
      */
-    public void dropExternalForeignKeys(Table table) throws IOException
+    public void dropForeignKeys(Table table) throws IOException
     {
         // no need to as we drop the table with CASCASE CONSTRAINTS
     }
@@ -254,7 +246,7 @@
     /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         // Index names in Oracle are unique to a schema and hence Oracle does not
         // use the ON <tablename> clause
@@ -365,160 +357,53 @@
     /**
      * {@inheritDoc}
      */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // While Oracle has an ALTER TABLE MODIFY statement, it is somewhat limited
-        // esp. if there is data in the table, so we don't use it
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // Oracle can only add not insert columns
-                // Also, we cannot add NOT NULL columns unless they have a default value
-                if (!addColumnChange.isAtEnd() ||
-                    (addColumnChange.getNewColumn().isRequired() && (addColumnChange.getNewColumn().getDefaultValue() == null)))
-                {
-                    // we need to rebuild the full table
-                    return;
-                }
-            }
-        }
-
-        // First we drop primary keys as necessary
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange       pkChange       = (PrimaryKeyChange)change;
-                RemovePrimaryKeyChange removePkChange = new RemovePrimaryKeyChange(pkChange.getChangedTable(),
-                                                                                   pkChange.getOldPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, removePkChange);
-            }
-        }
-
-        // Next we add/remove columns
-        // While Oracle has an ALTER TABLE MODIFY statement, it is somewhat limited
-        // esp. if there is data in the table, so we don't use it
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                processChange(currentModel, desiredModel, (AddColumnChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-        }
-        // Finally we add primary keys
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange    pkChange    = (PrimaryKeyChange)change;
-                AddPrimaryKeyChange addPkChange = new AddPrimaryKeyChange(pkChange.getChangedTable(),
-                                                                          pkChange.getNewPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, addPkChange);
-                changeIt.remove();
-            }
-        }
-    }
-
-    /**
-     * Processes the addition of a column to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void addColumn(Table table, Column newColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
+        writeColumn(table, newColumn);
         printEndOfStatement();
-        if (change.getNewColumn().isAutoIncrement())
+        if (newColumn.isAutoIncrement())
         {
-            createAutoIncrementSequence(change.getChangedTable(), change.getNewColumn());
-            createAutoIncrementTrigger(change.getChangedTable(), change.getNewColumn());
+            createAutoIncrementSequence(table, newColumn);
+            createAutoIncrementTrigger(table, newColumn);
         }
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
-        if (change.getChangedColumn().isAutoIncrement())
+        if (column.isAutoIncrement())
         {
-            dropAutoIncrementTrigger(change.getChangedTable(), change.getChangedColumn());
-            dropAutoIncrementSequence(change.getChangedTable(), change.getChangedColumn());
+            dropAutoIncrementTrigger(table, column);
+            dropAutoIncrementSequence(table, column);
         }
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a primary key from a table.
+     * Writes the SQL to drop the primary key of the given table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table The table
      */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
+    public void dropPrimaryKey(Table table) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP PRIMARY KEY");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/oracle/Oracle8Platform.java b/src/java/org/apache/ddlutils/platform/oracle/Oracle8Platform.java
index bf2582b..4284d6a 100644
--- a/src/java/org/apache/ddlutils/platform/oracle/Oracle8Platform.java
+++ b/src/java/org/apache/ddlutils/platform/oracle/Oracle8Platform.java
@@ -19,9 +19,23 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Types;

+import java.util.Map;

 

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -56,6 +70,7 @@
 

         info.setMaxIdentifierLength(30);

         info.setIdentityStatusReadingSupported(false);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

 

         // Note that the back-mappings are partially done by the model reader, not the driver

         info.addNativeTypeMapping(Types.ARRAY,         "BLOB",             Types.BLOB);

@@ -101,4 +116,108 @@
     {

         return DATABASENAME;

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        // While Oracle has an ALTER TABLE MODIFY statement, it is somewhat limited

+        // esp. if there is data in the table, so we don't use it

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if ((change instanceof AddPrimaryKeyChange) ||

+                    (change instanceof RemovePrimaryKeyChange))

+                {

+                    return true;

+                }

+                else if (change instanceof RemoveColumnChange)

+                {

+                    // TODO: for now we trigger recreating the table, but ideally we should simply add the necessary pk changes

+                    RemoveColumnChange removeColumnChange = (RemoveColumnChange)change;

+                    Column             column             = intermediateTable.findColumn(removeColumnChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+                    return !column.isPrimaryKey();

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // Oracle can only add not insert columns

+                    // Also, we cannot add NOT NULL columns unless they have a default value

+                    return addColumnChange.isAtEnd() &&

+                           (!addColumnChange.getNewColumn().isRequired() || (addColumnChange.getNewColumn().getDefaultValue() != null));

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((Oracle8Builder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((Oracle8Builder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the primary key 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,

+                              PrimaryKeyChange   change) throws IOException

+    {

+        Table    changedTable     = findChangedTable(currentModel, change);

+        String[] newPKColumnNames = change.getNewPrimaryKeyColumns();

+        Column[] newPKColumns     = new Column[newPKColumnNames.length];

+

+        for (int colIdx = 0; colIdx < newPKColumnNames.length; colIdx++)

+        {

+            newPKColumns[colIdx] = changedTable.findColumn(newPKColumnNames[colIdx], isDelimitedIdentifierModeOn());

+        }

+        

+        ((Oracle8Builder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        getSqlBuilder().createPrimaryKey(changedTable, newPKColumns);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlBuilder.java b/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlBuilder.java
index af54d73..602a3b9 100644
--- a/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlBuilder.java
@@ -20,14 +20,9 @@
  */
 
 import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.Index;
@@ -81,7 +76,7 @@
     /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         print("DROP INDEX ");
         printIdentifier(getIndexName(index));
@@ -173,89 +168,22 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // We can only use PostgreSQL-specific SQL if
-                // * the column is not set to NOT NULL (the constraint would be applied immediately
-                //   which will not work if there is already data in the table)
-                // * the column has no default value (it would be applied after the change which
-                //   means that PostgreSQL would behave differently from other databases where the
-                //   default is applied to every column)
-                // * the column is added at the end of the table (PostgreSQL does not support
-                //   insertion of a column)
-                if (!addColumnChange.getNewColumn().isRequired() &&
-                    (addColumnChange.getNewColumn().getDefaultValue() == null) &&
-                    (addColumnChange.getNextColumn() == null))
-                {
-                    processChange(currentModel, desiredModel, addColumnChange);
-                    changeIt.remove();
-                }
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-        }
-        super.processTableStructureChanges(currentModel, desiredModel, sourceTable, targetTable, parameters, changes);
-    }
-
-    /**
-     * Processes the addition of a column to a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
-        printIndent();
-        print("ADD COLUMN ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
-        printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-    }
-
-    /**
-     * Processes the removal of a column from a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
-    {
-        print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        if (change.getChangedColumn().isAutoIncrement())
+        if (column.isAutoIncrement())
         {
-            dropAutoIncrementSequence(change.getChangedTable(), change.getChangedColumn());
+            dropAutoIncrementSequence(table, column);
         }
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlPlatform.java b/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlPlatform.java
index 14b1367..de73cfe 100644
--- a/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/postgresql/PostgreSqlPlatform.java
@@ -19,6 +19,7 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Connection;

 import java.sql.DriverManager;

 import java.sql.PreparedStatement;

@@ -31,7 +32,16 @@
 import org.apache.commons.beanutils.DynaBean;

 import org.apache.ddlutils.DatabaseOperationException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

 import org.apache.ddlutils.dynabean.SqlDynaProperty;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -240,4 +250,61 @@
             super.setObject(statement, sqlIndex, dynaBean, property);

         }

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if (change instanceof RemoveColumnChange)

+                {

+                    return true;

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // We can only handle this if

+                    // * the column is not set to NOT NULL (the constraint would be applied immediately

+                    //   which will not work if there is already data in the table)

+                    // * the column has no default value (it would be applied after the change which

+                    //   means that PostgreSQL would behave differently from other databases where the

+                    //   default is applied to every column)

+                    // * the column is added at the end of the table (PostgreSQL does not support

+                    //   insertion of a column)

+                    return !addColumnChange.getNewColumn().isRequired() &&

+                           (addColumnChange.getNewColumn().getDefaultValue() == null) &&

+                           (addColumnChange.getNextColumn() == null);

+                }

+                else

+                {

+                    // TODO: PK changes ?

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((PostgreSqlBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/sapdb/SapDbBuilder.java b/src/java/org/apache/ddlutils/platform/sapdb/SapDbBuilder.java
index f5c3bbd..8865e57 100644
--- a/src/java/org/apache/ddlutils/platform/sapdb/SapDbBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/sapdb/SapDbBuilder.java
@@ -20,19 +20,8 @@
  */
 
 import java.io.IOException;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
-import org.apache.ddlutils.alteration.ColumnDefaultValueChange;
-import org.apache.ddlutils.alteration.ColumnRequiredChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
@@ -79,7 +68,7 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalPrimaryKeysCreateStmt(Table table, Column[] primaryKeyColumns) throws IOException
+    public void createPrimaryKey(Table table, Column[] primaryKeyColumns) throws IOException
     {
         // Note that SapDB does not support the addition of named primary keys
         if ((primaryKeyColumns.length > 0) && shouldGeneratePrimaryKeys(primaryKeyColumns))
@@ -96,24 +85,24 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ForeignKey key) throws IOException
+    public void createForeignKey(Database database, Table table, ForeignKey foreignKey) throws IOException
     {
-        if (key.getForeignTableName() == null)
+        if (foreignKey.getForeignTableName() == null)
         {
-            _log.warn("Foreign key table is null for key " + key);
+            _log.warn("Foreign key table is null for key " + foreignKey);
         }
         else
         {
             writeTableAlterStmt(table);
 
             print(" ADD FOREIGN KEY ");
-            printIdentifier(getForeignKeyName(table, key));
+            printIdentifier(getForeignKeyName(table, foreignKey));
             print(" (");
-            writeLocalReferences(key);
+            writeLocalReferences(foreignKey);
             print(") REFERENCES ");
-            printIdentifier(getTableName(database.findTable(key.getForeignTableName())));
+            printIdentifier(getTableName(database.findTable(foreignKey.getForeignTableName())));
             print(" (");
-            writeForeignReferences(key);
+            writeForeignReferences(foreignKey);
             print(")");
             printEndOfStatement();
         }
@@ -122,7 +111,7 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException
+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException
     {
         writeTableAlterStmt(table);
         print("DROP FOREIGN KEY ");
@@ -146,208 +135,90 @@
     /**
      * {@inheritDoc}
      */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // SapDB can only add not insert columns
-                if (!addColumnChange.isAtEnd())
-                {
-                    return;
-                }
-            }
-        }
-
-        // First we drop primary keys as necessary
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange       pkChange       = (PrimaryKeyChange)change;
-                RemovePrimaryKeyChange removePkChange = new RemovePrimaryKeyChange(pkChange.getChangedTable(),
-                                                                                   pkChange.getOldPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, removePkChange);
-            }
-        }
-        // Next we add/change/remove columns
-        // SapDB has a ALTER TABLE MODIFY COLUMN but it is limited regarding the type conversions
-        // it can perform, so we don't use it here but rather rebuild the table
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                processChange(currentModel, desiredModel, (AddColumnChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof ColumnDefaultValueChange)
-            {
-                processChange(currentModel, desiredModel, (ColumnDefaultValueChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof ColumnRequiredChange)
-            {
-                processChange(currentModel, desiredModel, (ColumnRequiredChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-        }
-        // Finally we add primary keys
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange    pkChange    = (PrimaryKeyChange)change;
-                AddPrimaryKeyChange addPkChange = new AddPrimaryKeyChange(pkChange.getChangedTable(),
-                                                                          pkChange.getNewPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, addPkChange);
-                changeIt.remove();
-            }
-        }
-    }
-
-    /**
-     * Processes the addition of a column to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void addColumn(Table table, Column newColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
+        writeColumn(table, newColumn);
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         print(" RELEASE SPACE");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a primary key from a table.
+     * Writes the SQL to drop the primary key of the given table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table The table
      */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
+    public void dropPrimaryKey(Table table) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP PRIMARY KEY");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the change of the required constraint of a column.
+     * Writes the SQL to set the required status of the given column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table      The table
+     * @param column     The column to change
+     * @param isRequired Whether the column shall be required
      */
-    protected void processChange(Database             currentModel,
-                                 Database             desiredModel,
-                                 ColumnRequiredChange change) throws IOException
+    public void changeColumnRequiredStatus(Table table, Column column, boolean isRequired) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
-        if (change.getChangedColumn().isRequired())
-        {
-            print(" DEFAULT NULL");
-        }
-        else
+        printIdentifier(getColumnName(column));
+        if (isRequired)
         {
             print(" NOT NULL");
         }
+        else
+        {
+            print(" DEFAULT NULL");
+        }
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the change of the default value of a column.
+     * Writes the SQL to set the default value of the given column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table           The table
+     * @param column          The column to change
+     * @param newDefaultValue The new default value
      */
-    protected void processChange(Database                 currentModel,
-                                 Database                 desiredModel,
-                                 ColumnDefaultValueChange change) throws IOException
+    public void changeColumnDefaultValue(Table table, Column column, String newDefaultValue) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("COLUMN ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
 
-        Table   curTable   = currentModel.findTable(change.getChangedTable().getName(), getPlatform().isDelimitedIdentifierModeOn());
-        Column  curColumn  = curTable.findColumn(change.getChangedColumn().getName(), getPlatform().isDelimitedIdentifierModeOn());
-        boolean hasDefault = curColumn.getParsedDefaultValue() != null;
+        boolean hasDefault = column.getParsedDefaultValue() != null;
 
-        if (isValidDefaultValue(change.getNewDefaultValue(), curColumn.getTypeCode()))
+        if (isValidDefaultValue(newDefaultValue, column.getTypeCode()))
         {
             if (hasDefault)
             {
@@ -357,13 +228,12 @@
             {
                 print(" ADD DEFAULT ");
             }
-            printDefaultValue(change.getNewDefaultValue(), curColumn.getTypeCode());
+            printDefaultValue(newDefaultValue, column.getTypeCode());
         }
         else if (hasDefault)
         {
             print(" DROP DEFAULT");
         }
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 }
diff --git a/src/java/org/apache/ddlutils/platform/sapdb/SapDbPlatform.java b/src/java/org/apache/ddlutils/platform/sapdb/SapDbPlatform.java
index d07a76a..59ca2dd 100644
--- a/src/java/org/apache/ddlutils/platform/sapdb/SapDbPlatform.java
+++ b/src/java/org/apache/ddlutils/platform/sapdb/SapDbPlatform.java
@@ -19,10 +19,26 @@
  * under the License.

  */

 

+import java.io.IOException;

 import java.sql.Types;

 

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ColumnDefinitionChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.PrimaryKeyChange;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

+import org.apache.ddlutils.util.StringUtils;

 

 /**

  * The SapDB platform implementation.

@@ -46,6 +62,7 @@
         PlatformInfo info = getPlatformInfo();

 

         info.setMaxIdentifierLength(32);

+        info.setPrimaryKeyColumnAutomaticallyRequired(true);

         info.setCommentPrefix("/*");

         info.setCommentSuffix("*/");

 

@@ -93,4 +110,159 @@
     {

         return DATABASENAME;

     }

+

+    /**

+     * Returns the model comparator for this platform.

+     * 

+     * @return The comparator

+     */

+    protected ModelComparator getModelComparator()

+    {

+        ModelComparator comparator = super.getModelComparator();

+

+        comparator.setCanDropPrimaryKeyColumns(false);

+        comparator.setGeneratePrimaryKeyChanges(false);

+        return comparator;

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof AddPrimaryKeyChange) ||

+                    (change instanceof PrimaryKeyChange) ||

+                    (change instanceof RemovePrimaryKeyChange))

+                {

+                    return true;

+                }

+                else if (change instanceof AddColumnChange) 

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // SapDB can only add not insert columns, and required columns have to have

+                    // a default value or be IDENTITY

+                    return (addColumnChange.getNextColumn() == null) &&

+                           (!addColumnChange.getNewColumn().isRequired() ||

+                            !StringUtils.isEmpty(addColumnChange.getNewColumn().getDefaultValue()));

+                }

+                else if (change instanceof ColumnDefinitionChange)

+                {

+                    ColumnDefinitionChange colChange = (ColumnDefinitionChange)change;

+

+                    // SapDB has a ALTER TABLE MODIFY COLUMN but it is limited regarding the type conversions

+                    // it can perform, so we don't use it here but rather rebuild the table

+                    Column curColumn = intermediateTable.findColumn(colChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+                    Column newColumn = colChange.getNewColumn();

+

+                    // we can however handle the change if only the default value or the required status was changed

+                    return ((curColumn.getTypeCode() == newColumn.getTypeCode()) &&

+                           (!getPlatformInfo().hasSize(curColumn.getTypeCode()) || StringUtils.equals(curColumn.getSize(), newColumn.getSize())) &&

+                           (curColumn.isAutoIncrement() == newColumn.isAutoIncrement()));

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((SapDbBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((SapDbBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the primary key 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,

+                              PrimaryKeyChange   change) throws IOException

+    {

+        Table    changedTable     = findChangedTable(currentModel, change);

+        String[] newPKColumnNames = change.getNewPrimaryKeyColumns();

+        Column[] newPKColumns     = new Column[newPKColumnNames.length];

+

+        for (int colIdx = 0; colIdx < newPKColumnNames.length; colIdx++)

+        {

+            newPKColumns[colIdx] = changedTable.findColumn(newPKColumnNames[colIdx], isDelimitedIdentifierModeOn());

+        }

+        

+        ((SapDbBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        getSqlBuilder().createPrimaryKey(changedTable, newPKColumns);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the change of the column 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,

+                              ColumnDefinitionChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column changedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        if (!StringUtils.equals(changedColumn.getDefaultValue(), change.getNewColumn().getDefaultValue()))

+        {

+            ((SapDbBuilder)getSqlBuilder()).changeColumnDefaultValue(changedTable,

+                                                                  changedColumn,

+                                                                  change.getNewColumn().getDefaultValue());

+        }

+        if (changedColumn.isRequired() != change.getNewColumn().isRequired())

+        {

+            ((SapDbBuilder)getSqlBuilder()).changeColumnRequiredStatus(changedTable,

+                                                                    changedColumn,

+                                                                    change.getNewColumn().isRequired());

+        }

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/platform/sybase/SybaseBuilder.java b/src/java/org/apache/ddlutils/platform/sybase/SybaseBuilder.java
index 99c36c7..fdc40fa 100644
--- a/src/java/org/apache/ddlutils/platform/sybase/SybaseBuilder.java
+++ b/src/java/org/apache/ddlutils/platform/sybase/SybaseBuilder.java
@@ -21,30 +21,18 @@
 
 import java.io.IOException;
 import java.sql.Types;
-import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.ddlutils.Platform;
-import org.apache.ddlutils.alteration.AddColumnChange;
-import org.apache.ddlutils.alteration.AddPrimaryKeyChange;
-import org.apache.ddlutils.alteration.ColumnAutoIncrementChange;
-import org.apache.ddlutils.alteration.ColumnChange;
-import org.apache.ddlutils.alteration.ColumnDefaultValueChange;
-import org.apache.ddlutils.alteration.PrimaryKeyChange;
-import org.apache.ddlutils.alteration.RemoveColumnChange;
-import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;
-import org.apache.ddlutils.alteration.TableChange;
 import org.apache.ddlutils.model.Column;
 import org.apache.ddlutils.model.Database;
 import org.apache.ddlutils.model.ForeignKey;
 import org.apache.ddlutils.model.Index;
 import org.apache.ddlutils.model.Table;
-import org.apache.ddlutils.platform.CreationParameters;
 import org.apache.ddlutils.platform.SqlBuilder;
 import org.apache.ddlutils.util.Jdbc3Utils;
+import org.apache.ddlutils.util.StringUtils;
 
 /**
  * The SQL Builder for Sybase.
@@ -69,7 +57,7 @@
      */
     public void createTable(Database database, Table table, Map parameters) throws IOException
     {
-        writeQuotationOnStatement();
+        turnOnQuotation();
         super.createTable(database, table, parameters);
     }
 
@@ -196,7 +184,7 @@
      */
     public void dropTable(Table table) throws IOException
     {
-        writeQuotationOnStatement();
+        turnOnQuotation();
         print("IF EXISTS (SELECT 1 FROM sysobjects WHERE type = 'U' AND name = ");
         printAlwaysSingleQuotedIdentifier(getTableName(table));
         println(")");
@@ -211,7 +199,7 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeExternalForeignKeyDropStmt(Table table, ForeignKey foreignKey) throws IOException
+    public void dropForeignKey(Table table, ForeignKey foreignKey) throws IOException
     {
         String constraintName = getForeignKeyName(table, foreignKey);
 
@@ -229,7 +217,7 @@
     /**
      * {@inheritDoc}
      */
-    public void writeExternalIndexDropStmt(Table table, Index index) throws IOException
+    public void dropIndex(Table table, Index index) throws IOException
     {
         print("DROP INDEX ");
         printIdentifier(getTableName(table));
@@ -241,10 +229,10 @@
     /**
      * {@inheritDoc}
      */
-    public void dropExternalForeignKeys(Table table) throws IOException
+    public void dropForeignKeys(Table table) throws IOException
     {
-        writeQuotationOnStatement();
-        super.dropExternalForeignKeys(table);
+        turnOnQuotation();
+        super.dropForeignKeys(table);
     }
 
     /**
@@ -309,9 +297,36 @@
     /**
      * Writes the statement that turns on the ability to write delimited identifiers.
      */
-    private void writeQuotationOnStatement() throws IOException
+    public void turnOnQuotation() throws IOException
     {
-        print(getQuotationOnStatement());
+        String quotationStmt = getQuotationOnStatement();
+
+        if (!StringUtils.isEmpty(quotationStmt))
+        {
+            print(quotationStmt);
+            printEndOfStatement();
+        }
+    }
+
+    /**
+     * Writes the statement that turns on identity override mode.
+     * 
+     * @param table The table to enable the mode for
+     */
+    public void turnOnIdentityOverride(Table table) throws IOException
+    {
+        print(getEnableIdentityOverrideSql(table));
+        printEndOfStatement();
+    }
+
+    /**
+     * Writes the statement that turns off identity override mode.
+     * 
+     * @param table The table to disable the mode for
+     */
+    public void turnOffIdentityOverride(Table table) throws IOException
+    {
+        print(getDisableIdentityOverrideSql(table));
         printEndOfStatement();
     }
 
@@ -331,23 +346,30 @@
     /**
      * {@inheritDoc}
      */
-    protected void writeCopyDataStatement(Table sourceTable, Table targetTable) throws IOException
+    protected void copyData(Table sourceTable, Table targetTable) throws IOException
     {
-        boolean hasIdentity = targetTable.getAutoIncrementColumns().length > 0;
+        // We need to turn on identity override except when the identity column was added to the column
+        Column[] targetAutoIncrCols   = targetTable.getAutoIncrementColumns();
+        boolean  needIdentityOverride = false;
 
-        if (hasIdentity)
+        if (targetAutoIncrCols.length > 0)
         {
-            print("SET IDENTITY_INSERT ");
-            printIdentifier(getTableName(targetTable));
-            print(" ON");
+            needIdentityOverride = true;
+            // Sybase only allows for one identity column per table
+            if (sourceTable.findColumn(targetAutoIncrCols[0].getName(), getPlatform().isDelimitedIdentifierModeOn()) == null)
+            {
+                needIdentityOverride = false;
+            }
+        }
+        if (needIdentityOverride)
+        {
+            print(getEnableIdentityOverrideSql(targetTable));
             printEndOfStatement();
         }
-        super.writeCopyDataStatement(sourceTable, targetTable);
-        if (hasIdentity)
+        super.copyData(sourceTable, targetTable);
+        if (needIdentityOverride)
         {
-            print("SET IDENTITY_INSERT ");
-            printIdentifier(getTableName(targetTable));
-            print(" OFF");
+            print(getDisableIdentityOverrideSql(targetTable));
             printEndOfStatement();
         }
     }
@@ -377,198 +399,42 @@
     /**
      * {@inheritDoc}
      */
-    protected void processChanges(Database currentModel, Database desiredModel, List changes, CreationParameters params) throws IOException
-    {
-        if (!changes.isEmpty())
-        {
-            writeQuotationOnStatement();
-        }
-        super.processChanges(currentModel, desiredModel, changes, params);
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    protected void processTableStructureChanges(Database currentModel,
-                                                Database desiredModel,
-                                                Table    sourceTable,
-                                                Table    targetTable,
-                                                Map      parameters,
-                                                List     changes) throws IOException
-    {
-        // First we drop primary keys as necessary
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof RemovePrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (RemovePrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange       pkChange       = (PrimaryKeyChange)change;
-                RemovePrimaryKeyChange removePkChange = new RemovePrimaryKeyChange(pkChange.getChangedTable(),
-                                                                                   pkChange.getOldPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, removePkChange);
-            }
-        }
-
-
-        HashMap columnChanges = new HashMap();
-
-        // Next we add/remove columns
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddColumnChange)
-            {
-                AddColumnChange addColumnChange = (AddColumnChange)change;
-
-                // Sybase can only add not insert columns
-                if (addColumnChange.isAtEnd())
-                {
-                    processChange(currentModel, desiredModel, addColumnChange);
-                    changeIt.remove();
-                }
-            }
-            else if (change instanceof RemoveColumnChange)
-            {
-                processChange(currentModel, desiredModel, (RemoveColumnChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof ColumnAutoIncrementChange)
-            {
-                // Sybase has no way of adding or removing an IDENTITY constraint
-                // Thus we have to rebuild the table anyway and can ignore all the other 
-                // column changes
-                columnChanges = null;
-            }
-            else if ((change instanceof ColumnChange) && (columnChanges != null))
-            {
-                // we gather all changed columns because we can use the ALTER TABLE ALTER COLUMN
-                // statement for them
-                ColumnChange columnChange     = (ColumnChange)change;
-                ArrayList    changesPerColumn = (ArrayList)columnChanges.get(columnChange.getChangedColumn());
-
-                if (changesPerColumn == null)
-                {
-                    changesPerColumn = new ArrayList();
-                    columnChanges.put(columnChange.getChangedColumn(), changesPerColumn);
-                }
-                changesPerColumn.add(change);
-            }
-        }
-        if (columnChanges != null)
-        {
-            for (Iterator changesPerColumnIt = columnChanges.entrySet().iterator(); changesPerColumnIt.hasNext();)
-            {
-                Map.Entry entry            = (Map.Entry)changesPerColumnIt.next();
-                Column    sourceColumn     = (Column)entry.getKey();
-                ArrayList changesPerColumn = (ArrayList)entry.getValue();
-
-                // Sybase does not like us to use the ALTER TABLE ALTER statement if we don't actually
-                // change the datatype or the required constraint but only the default value
-                // Thus, if we only have to change the default, we use a different handler
-                if ((changesPerColumn.size() == 1) && (changesPerColumn.get(0) instanceof ColumnDefaultValueChange))
-                {
-                    processChange(currentModel,
-                                  desiredModel,
-                                  (ColumnDefaultValueChange)changesPerColumn.get(0));
-                }
-                else
-                {
-                    Column targetColumn = targetTable.findColumn(sourceColumn.getName(),
-                                                                 getPlatform().isDelimitedIdentifierModeOn());
-
-                    processColumnChange(sourceTable, targetTable, sourceColumn, targetColumn);
-                }
-                for (Iterator changeIt = changesPerColumn.iterator(); changeIt.hasNext();)
-                {
-                    ((ColumnChange)changeIt.next()).apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
-                }
-            }
-        }
-        // Finally we add primary keys
-        for (Iterator changeIt = changes.iterator(); changeIt.hasNext();)
-        {
-            TableChange change = (TableChange)changeIt.next();
-
-            if (change instanceof AddPrimaryKeyChange)
-            {
-                processChange(currentModel, desiredModel, (AddPrimaryKeyChange)change);
-                changeIt.remove();
-            }
-            else if (change instanceof PrimaryKeyChange)
-            {
-                PrimaryKeyChange    pkChange    = (PrimaryKeyChange)change;
-                AddPrimaryKeyChange addPkChange = new AddPrimaryKeyChange(pkChange.getChangedTable(),
-                                                                          pkChange.getNewPrimaryKeyColumns());
-
-                processChange(currentModel, desiredModel, addPkChange);
-                changeIt.remove();
-            }
-        }
-    }
-
-
-    /**
-     * Processes the addition of a column to a table.
-     * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
-     */
-    protected void processChange(Database        currentModel,
-                                 Database        desiredModel,
-                                 AddColumnChange change) throws IOException
+    public void addColumn(Table table, Column newColumn) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("ADD ");
-        writeColumn(change.getChangedTable(), change.getNewColumn());
+        writeColumn(table, newColumn);
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a column from a table.
+     * Writes the SQL to drop a column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table  The table
+     * @param column The column to drop
      */
-    protected void processChange(Database           currentModel,
-                                 Database           desiredModel,
-                                 RemoveColumnChange change) throws IOException
+    public void dropColumn(Table table, Column column) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("DROP ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
+        printIdentifier(getColumnName(column));
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the removal of a primary key from a table.
+     * Writes the SQL to drop the primary key of the given table.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table The table
      */
-    protected void processChange(Database               currentModel,
-                                 Database               desiredModel,
-                                 RemovePrimaryKeyChange change) throws IOException
+    public void dropPrimaryKey(Table table) throws IOException
     {
-        // TODO: this would be easier when named primary keys are supported
-        //       because then we can use ALTER TABLE DROP
-        String tableName         = getTableName(change.getChangedTable());
+        // this would be easier when named primary keys are supported
+        // because then we can use ALTER TABLE DROP
+        String tableName         = getTableName(table);
         String tableNameVar      = "tn" + createUniqueIdentifier();
         String constraintNameVar = "cn" + createUniqueIdentifier();
 
@@ -589,96 +455,87 @@
         println("  END");
         print("END");
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes the change of the default value of a column. Note that this method is only
-     * used if it is the only change to that column.
+     * Writes the SQL to set the default value of the given column.
      * 
-     * @param currentModel The current database schema
-     * @param desiredModel The desired database schema
-     * @param change       The change object
+     * @param table           The table
+     * @param column          The column to change
+     * @param newDefaultValue The new default value
      */
-    protected void processChange(Database                 currentModel,
-                                 Database                 desiredModel,
-                                 ColumnDefaultValueChange change) throws IOException
+    public void changeColumnDefaultValue(Table table, Column column, String newDefaultValue) throws IOException
     {
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(change.getChangedTable()));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("REPLACE ");
-        printIdentifier(getColumnName(change.getChangedColumn()));
-
-        Table  curTable  = currentModel.findTable(change.getChangedTable().getName(), getPlatform().isDelimitedIdentifierModeOn());
-        Column curColumn = curTable.findColumn(change.getChangedColumn().getName(), getPlatform().isDelimitedIdentifierModeOn());
-
+        printIdentifier(getColumnName(column));
         print(" DEFAULT ");
-        if (isValidDefaultValue(change.getNewDefaultValue(), curColumn.getTypeCode()))
+        if (isValidDefaultValue(newDefaultValue, column.getTypeCode()))
         {
-            printDefaultValue(change.getNewDefaultValue(), curColumn.getTypeCode());
+            printDefaultValue(newDefaultValue, column.getTypeCode());
         }
         else
         {
             print("NULL");
         }
         printEndOfStatement();
-        change.apply(currentModel, getPlatform().isDelimitedIdentifierModeOn());
     }
 
     /**
-     * Processes a change to a column.
+     * Writes the SQL to change the given column.
      * 
-     * @param sourceTable  The current table
-     * @param targetTable  The desired table
-     * @param sourceColumn The current column
-     * @param targetColumn The desired column
+     * @param table     The table
+     * @param column    The column to change
+     * @param newColumn The new column definition
      */
-    protected void processColumnChange(Table  sourceTable,
-                                       Table  targetTable,
-                                       Column sourceColumn,
-                                       Column targetColumn) throws IOException
+    public void changeColumn(Table table, Column column, Column newColumn) throws IOException
     {
-        Object oldParsedDefault = sourceColumn.getParsedDefaultValue();
-        Object newParsedDefault = targetColumn.getParsedDefaultValue();
-        String newDefault       = targetColumn.getDefaultValue();
+        Object oldParsedDefault = column.getParsedDefaultValue();
+        Object newParsedDefault = newColumn.getParsedDefaultValue();
+        String newDefault       = newColumn.getDefaultValue();
         boolean defaultChanges  = ((oldParsedDefault == null) && (newParsedDefault != null)) ||
                                   ((oldParsedDefault != null) && !oldParsedDefault.equals(newParsedDefault));
 
         // Sybase does not like it if there is a default spec in the ALTER TABLE ALTER
         // statement; thus we have to change the default afterwards
-        if (newDefault != null)
-        {
-            targetColumn.setDefaultValue(null);
-        }
         if (defaultChanges)
         {
             // we're first removing the default as it might make problems when the
             // datatype changes
             print("ALTER TABLE ");
-            printlnIdentifier(getTableName(sourceTable));
+            printlnIdentifier(getTableName(table));
             printIndent();
             print("REPLACE ");
-            printIdentifier(getColumnName(sourceColumn));
+            printIdentifier(getColumnName(column));
             print(" DEFAULT NULL");
             printEndOfStatement();
         }
         print("ALTER TABLE ");
-        printlnIdentifier(getTableName(sourceTable));
+        printlnIdentifier(getTableName(table));
         printIndent();
         print("MODIFY ");
-        writeColumn(sourceTable, targetColumn);
+        if (newDefault != null)
+        {
+            newColumn.setDefaultValue(null);
+        }
+        writeColumn(table, newColumn);
+        if (newDefault != null)
+        {
+            newColumn.setDefaultValue(newDefault);
+        }
         printEndOfStatement();
         if (defaultChanges)
         {
             print("ALTER TABLE ");
-            printlnIdentifier(getTableName(sourceTable));
+            printlnIdentifier(getTableName(table));
             printIndent();
             print("REPLACE ");
-            printIdentifier(getColumnName(sourceColumn));
+            printIdentifier(getColumnName(column));
             if (newDefault != null)
             {
-                writeColumnDefaultValueStmt(sourceTable, targetColumn);
+                writeColumnDefaultValueStmt(table, newColumn);
             }
             else
             {
diff --git a/src/java/org/apache/ddlutils/platform/sybase/SybasePlatform.java b/src/java/org/apache/ddlutils/platform/sybase/SybasePlatform.java
index db298a0..a9e4a68 100644
--- a/src/java/org/apache/ddlutils/platform/sybase/SybasePlatform.java
+++ b/src/java/org/apache/ddlutils/platform/sybase/SybasePlatform.java
@@ -32,11 +32,24 @@
 import java.util.Iterator;

 import java.util.List;

 

+import org.apache.commons.lang.StringUtils;

 import org.apache.ddlutils.DatabaseOperationException;

+import org.apache.ddlutils.DdlUtilsException;

 import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.alteration.AddColumnChange;

+import org.apache.ddlutils.alteration.AddPrimaryKeyChange;

+import org.apache.ddlutils.alteration.ColumnDefinitionChange;

+import org.apache.ddlutils.alteration.ModelComparator;

+import org.apache.ddlutils.alteration.RemoveColumnChange;

+import org.apache.ddlutils.alteration.RemovePrimaryKeyChange;

+import org.apache.ddlutils.alteration.TableChange;

+import org.apache.ddlutils.alteration.TableDefinitionChangesPredicate;

+import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.Table;

 import org.apache.ddlutils.model.TypeMap;

+import org.apache.ddlutils.platform.CreationParameters;

+import org.apache.ddlutils.platform.DefaultTableDefinitionChangesPredicate;

 import org.apache.ddlutils.platform.PlatformImplBase;

 

 /**

@@ -67,6 +80,7 @@
 

         info.setMaxIdentifierLength(28);

         info.setNullAsDefaultValueRequired(true);

+        info.setIdentityColumnAutomaticallyRequired(true);

         info.setCommentPrefix("/*");

         info.setCommentSuffix("*/");

 

@@ -332,4 +346,141 @@
     {

         afterInsert(connection, table);

     }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected ModelComparator getModelComparator()

+    {

+        ModelComparator comparator = super.getModelComparator();

+

+        comparator.setGeneratePrimaryKeyChanges(false);

+        comparator.setCanDropPrimaryKeyColumns(false);

+        return comparator;

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+    {

+        return new DefaultTableDefinitionChangesPredicate()

+        {

+            protected boolean isSupported(Table intermediateTable, TableChange change)

+            {

+                if ((change instanceof RemoveColumnChange) ||

+                    (change instanceof AddPrimaryKeyChange) ||

+                    (change instanceof RemovePrimaryKeyChange))

+                {

+                    return true;

+                }

+                else if (change instanceof AddColumnChange)

+                {

+                    AddColumnChange addColumnChange = (AddColumnChange)change;

+

+                    // Sybase can only add not insert columns, and they cannot be IDENTITY columns

+                    // We also have to force recreation of the table if a required column is added

+                    // that is neither IDENTITY nor has a default value

+                    return (addColumnChange.getNextColumn() == null) &&

+                           !addColumnChange.getNewColumn().isAutoIncrement() &&

+                           (!addColumnChange.getNewColumn().isRequired() || !StringUtils.isEmpty(addColumnChange.getNewColumn().getDefaultValue()));

+                }

+                else if (change instanceof ColumnDefinitionChange)

+                {

+                    ColumnDefinitionChange columnChange = (ColumnDefinitionChange)change;

+                    Column                 oldColumn    = intermediateTable.findColumn(columnChange.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+                    // Sybase cannot change the IDENTITY state of a column via ALTER TABLE MODIFY

+                    return oldColumn.isAutoIncrement() == columnChange.getNewColumn().isAutoIncrement();

+                }

+                else

+                {

+                    return false;

+                }

+            }

+        };

+    }

+

+    /**

+     * {@inheritDoc}

+     */

+    protected Database processChanges(Database model, Collection changes, CreationParameters params) throws IOException, DdlUtilsException

+    {

+        if (!changes.isEmpty())

+        {

+            ((SybaseBuilder)getSqlBuilder()).turnOnQuotation();

+        }

+

+        return super.processChanges(model, changes, params);

+    }

+

+    /**

+     * Processes the removal of a column from 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,

+                              RemoveColumnChange change) throws IOException

+    {

+        Table  changedTable  = findChangedTable(currentModel, change);

+        Column removedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+

+        ((SybaseBuilder)getSqlBuilder()).dropColumn(changedTable, removedColumn);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+    /**

+     * Processes the removal of a primary key from 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,

+                              RemovePrimaryKeyChange change) throws IOException

+    {

+        Table changedTable = findChangedTable(currentModel, change);

+

+        ((SybaseBuilder)getSqlBuilder()).dropPrimaryKey(changedTable);

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

+

+

+    /**

+     * Processes the change of a column definition..

+     * 

+     * @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,

+                              ColumnDefinitionChange change) throws IOException

+    {

+        Table         changedTable  = findChangedTable(currentModel, change);

+        Column        changedColumn = changedTable.findColumn(change.getChangedColumn(), isDelimitedIdentifierModeOn());

+        Column        newColumn     = change.getNewColumn();

+        SybaseBuilder sqlBuilder    = (SybaseBuilder)getSqlBuilder();

+

+        // if we only change the default value, then we need to use different SQL

+        if (!ColumnDefinitionChange.isTypeChanged(getPlatformInfo(), changedColumn, newColumn) &&

+            !ColumnDefinitionChange.isSizeChanged(getPlatformInfo(), changedColumn, newColumn) &&

+            !ColumnDefinitionChange.isRequiredStatusChanged(changedColumn, newColumn) &&

+            !ColumnDefinitionChange.isAutoIncrementChanged(changedColumn, newColumn))

+        {

+            sqlBuilder.changeColumnDefaultValue(changedTable, changedColumn, newColumn.getDefaultValue());

+        }

+        else

+        {

+            sqlBuilder.changeColumn(changedTable, changedColumn, newColumn);

+        }

+        change.apply(currentModel, isDelimitedIdentifierModeOn());

+    }

 }

diff --git a/src/java/org/apache/ddlutils/task/WriteSchemaSqlToFileCommand.java b/src/java/org/apache/ddlutils/task/WriteSchemaSqlToFileCommand.java
index 682cd1b..7b8b594 100644
--- a/src/java/org/apache/ddlutils/task/WriteSchemaSqlToFileCommand.java
+++ b/src/java/org/apache/ddlutils/task/WriteSchemaSqlToFileCommand.java
@@ -21,6 +21,7 @@
 

 import java.io.File;

 import java.io.FileWriter;

+import java.io.IOException;

 import java.sql.Connection;

 import java.sql.SQLException;

 

@@ -121,10 +122,11 @@
         Platform           platform        = getPlatform();

         boolean            isCaseSensitive = platform.isDelimitedIdentifierModeOn();

         CreationParameters params          = getFilteredParameters(model, platform.getName(), isCaseSensitive);

+        FileWriter         writer          = null;

 

         try

         {

-            FileWriter writer = new FileWriter(_outputFile);

+            writer = new FileWriter(_outputFile);

 

             platform.setScriptModeOn(true);

             if (platform.getPlatformInfo().isSqlCommentsSupported())

@@ -132,7 +134,6 @@
                 // we're generating SQL comments if possible

                 platform.setSqlCommentsOn(true);

             }

-            platform.getSqlBuilder().setWriter(writer);

 

             boolean shouldAlter = isAlterDatabase();

 

@@ -167,18 +168,31 @@
                                              platform.readModelFromDatabase("unnamed", getCatalogPattern(), getSchemaPattern(), null) :

                                              platform.readModelFromDatabase("unnamed");

 

-                platform.getSqlBuilder().alterDatabase(currentModel, model, params);

+                writer.write(platform.getAlterModelSql(currentModel, model, params));

             }

             else

             {

-                platform.getSqlBuilder().createTables(model, params, _doDrops);

+                writer.write(platform.getCreateModelSql(model, params, _doDrops, !isFailOnError()));

             }

-            writer.close();

             _log.info("Written schema SQL to " + _outputFile.getAbsolutePath());

         }

         catch (Exception ex)

         {

             handleException(ex, ex.getMessage());

         }

+        finally

+        {

+            if (writer != null)

+            {

+                try

+                {

+                    writer.close();

+                }

+                catch (IOException ex)

+                {

+                    _log.error("Could not close file " + _outputFile.getAbsolutePath(), ex);

+                }

+            }

+        }

     }

 }

diff --git a/src/java/org/apache/ddlutils/util/StringUtils.java b/src/java/org/apache/ddlutils/util/StringUtils.java
new file mode 100644
index 0000000..17b9e77
--- /dev/null
+++ b/src/java/org/apache/ddlutils/util/StringUtils.java
@@ -0,0 +1,42 @@
+package org.apache.ddlutils.util;
+
+/*
+ * 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.
+ */
+
+/**
+ * Helper class containing string utility functions.
+ * 
+ * @version $Revision: $
+ */
+public class StringUtils extends org.apache.commons.lang.StringUtils
+{
+    /**
+     * Compares the two given strings in a case sensitive or insensitive manner
+     * depending on the <code>caseSensitive</code> parameter.
+     * 
+     * @param strA          The first string
+     * @param strB          The second string
+     * @param caseSensitive Whether case matters in the comparison
+     * @return <code>true</code> if the two strings are equal
+     */
+    public static final boolean equals(String strA, String strB, boolean caseSensitive)
+    {
+        return caseSensitive ? equals(strA, strB) : equalsIgnoreCase(strA, strB);
+    }
+}
diff --git a/src/test/org/apache/ddlutils/RunAllTests.java b/src/test/org/apache/ddlutils/RunAllTests.java
index 3479698..5256107 100644
--- a/src/test/org/apache/ddlutils/RunAllTests.java
+++ b/src/test/org/apache/ddlutils/RunAllTests.java
@@ -20,13 +20,15 @@
  */

 

 import org.apache.ddlutils.alteration.TestAlterationAlgorithm;

-import org.apache.ddlutils.alteration.TestModelComparator;

+import org.apache.ddlutils.alteration.TestModelComparison;

 import org.apache.ddlutils.dynabean.TestDynaSqlQueries;

+import org.apache.ddlutils.io.TestAddColumn;

 import org.apache.ddlutils.io.TestAlteration;

 import org.apache.ddlutils.io.TestConstraints;

 import org.apache.ddlutils.io.TestDataReaderAndWriter;

 import org.apache.ddlutils.io.TestDatabaseIO;

 import org.apache.ddlutils.io.TestDatatypes;

+import org.apache.ddlutils.io.TestDropColumn;

 import org.apache.ddlutils.io.TestMisc;

 import org.apache.ddlutils.io.converters.TestDateConverter;

 import org.apache.ddlutils.io.converters.TestTimeConverter;

@@ -116,7 +118,7 @@
         suite.addTestSuite(TestPostgresqlPlatform.class);

         suite.addTestSuite(TestSapDbPlatform.class);

         suite.addTestSuite(TestSybasePlatform.class);

-        suite.addTestSuite(TestModelComparator.class);

+        suite.addTestSuite(TestModelComparison.class);

         suite.addTestSuite(TestAlterationAlgorithm.class);

 

         // tests that need a live database

@@ -126,6 +128,8 @@
             suite.addTestSuite(TestDatatypes.class);

             suite.addTestSuite(TestConstraints.class);

             suite.addTestSuite(TestAlteration.class);

+            suite.addTestSuite(TestAddColumn.class);

+            suite.addTestSuite(TestDropColumn.class);

             suite.addTestSuite(TestMisc.class);

         }

 

diff --git a/src/test/org/apache/ddlutils/alteration/TestAlterationAlgorithm.java b/src/test/org/apache/ddlutils/alteration/TestAlterationAlgorithm.java
index 81e7aed..8da0679 100644
--- a/src/test/org/apache/ddlutils/alteration/TestAlterationAlgorithm.java
+++ b/src/test/org/apache/ddlutils/alteration/TestAlterationAlgorithm.java
@@ -20,7 +20,6 @@
  */

 

 import java.io.IOException;

-import java.io.StringWriter;

 

 import org.apache.ddlutils.Platform;

 import org.apache.ddlutils.TestBase;

@@ -36,17 +35,13 @@
 {

     /** The tested platform. */

     private Platform _platform;

-    /** The writer that the builder of the platform writes to. */

-    private StringWriter _writer;

 

     /**

      * {@inheritDoc}

      */

     protected void setUp() throws Exception

     {

-        _writer   = new StringWriter();

         _platform = new TestPlatform();

-        _platform.getSqlBuilder().setWriter(_writer);

         _platform.setSqlCommentsOn(false);

         _platform.setDelimitedIdentifierModeOn(true);

     }

@@ -57,7 +52,6 @@
     protected void tearDown() throws Exception

     {

         _platform = null;

-        _writer   = null;

     }

 

     /**

@@ -67,14 +61,12 @@
      * @param desiredSchema The desired schema XML 

      * @return The sql

      */

-    protected String getAlterDatabaseSQL(String currentSchema, String desiredSchema) throws IOException

+    protected String getAlterModelSQL(String currentSchema, String desiredSchema) throws IOException

     {

         Database currentModel = parseDatabaseFromString(currentSchema);

         Database desiredModel = parseDatabaseFromString(desiredSchema);

 

-        _platform.getSqlBuilder().alterDatabase(currentModel, desiredModel, null);

-

-        return _writer.toString();

+        return _platform.getAlterModelSql(currentModel, desiredModel);

     }

 

     /**

@@ -102,7 +94,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "",

-            getAlterDatabaseSQL(modelXml, modelXml));

+            getAlterModelSQL(modelXml, modelXml));

     }

 

     /**

@@ -134,7 +126,7 @@
             "    \"COLPK\" INTEGER NOT NULL,\n"+

             "    PRIMARY KEY (\"COLPK\")\n"+

             ");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -172,7 +164,7 @@
             "    PRIMARY KEY (\"COLPK\")\n"+

             ");\n"+

             "CREATE INDEX \"TESTINDEX\" ON \"TABLEB\" (\"COL\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -210,7 +202,7 @@
             "    PRIMARY KEY (\"COLPK\")\n"+

             ");\n"+

             "CREATE UNIQUE INDEX \"TESTINDEX\" ON \"TABLEB\" (\"COL\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -248,7 +240,7 @@
             "    PRIMARY KEY (\"COLPK\")\n"+

             ");\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -294,7 +286,7 @@
             ");\n"+

             "ALTER TABLE \"TableA\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"ColFK\") REFERENCES \"TABLEB\" (\"COLPK\");\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -322,7 +314,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP TABLE \"TableA\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -354,7 +346,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP TABLE \"TableA\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -387,7 +379,7 @@
         assertEqualsIgnoringWhitespaces(

             "ALTER TABLE \"TableA\" DROP CONSTRAINT \"TESTFK\";\n"+

             "DROP TABLE \"TableA\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -421,7 +413,7 @@
         assertEqualsIgnoringWhitespaces(

             "ALTER TABLE \"TableA\" DROP CONSTRAINT \"TESTFK\";\n"+

             "DROP TABLE \"TABLEB\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -457,7 +449,7 @@
             "ALTER TABLE \"TABLEB\" DROP CONSTRAINT \"TESTFK\";\n"+

             "DROP TABLE \"TableA\";\n"+

             "DROP TABLE \"TABLEB\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -487,7 +479,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -517,7 +509,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "CREATE UNIQUE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -547,7 +539,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TestIndex\" ON \"TableA\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -577,7 +569,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TestIndex\" ON \"TableA\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -604,7 +596,7 @@
 

         assertEqualsIgnoringWhitespaces(

             "ALTER TABLE \"TableA\" ADD CONSTRAINT \"TableA_PK\" PRIMARY KEY (\"ColPK1\",\"ColPK2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -631,25 +623,9 @@
             "</database>";

 

         assertEqualsIgnoringWhitespaces(

-            "CREATE TABLE \"TableA_\"\n"+

-            "(\n"+

-            "    \"ColPK1\" INTEGER NOT NULL,\n"+

-            "    \"ColPK2\" VARCHAR(64) NOT NULL,\n"+

-            "    \"Col\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"ColPK1\",\"ColPK2\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA_\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA\";\n"+

-            "DROP TABLE \"TableA\";\n"+

-            "CREATE TABLE \"TableA\"\n"+

-            "(\n"+

-            "    \"ColPK1\" INTEGER NOT NULL,\n"+

-            "    \"ColPK2\" VARCHAR(64) NOT NULL,\n"+

-            "    \"Col\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"ColPK1\",\"ColPK2\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\",\"Col\") SELECT \"ColPK1\",\"ColPK2\",\"Col\" FROM \"TableA_\";\n"+

-            "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col\" DOUBLE;\n"+

+            "ALTER TABLE \"TableA\" ADD CONSTRAINT \"TableA_PK\" PRIMARY KEY (\"ColPK1\",\"ColPK2\");\n",

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -689,7 +665,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\", \"ColPK2\") SELECT \"ColPK1\", \"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -714,23 +690,8 @@
             "</database>";

 

         assertEqualsIgnoringWhitespaces(

-            "CREATE TABLE \"TableA_\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA_\" (\"ColPK\") SELECT \"ColPK\" FROM \"TableA\";\n"+

-            "DROP TABLE \"TableA\";\n"+

-            "CREATE TABLE \"TableA\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK\", \"Col\") SELECT \"ColPK\", \"Col\" FROM \"TableA_\";\n"+

-            "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col\" VARCHAR(64);\n",

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -769,7 +730,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\") SELECT \"ColPK\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -810,7 +771,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\", \"ColPK2\") SELECT \"ColPK1\", \"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -849,7 +810,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\") SELECT \"ColPK1\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -908,26 +869,9 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

-            "CREATE TABLE \"TABLEB_\"\n"+

-            "(\n"+

-            "    \"COLPK\" DOUBLE NOT NULL,\n"+

-            "    \"COLFK1\" INTEGER,\n"+

-            "    \"COLFK2\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"COLPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TABLEB_\" (\"COLPK\",\"COLFK1\") SELECT \"COLPK\",\"COLFK1\" FROM \"TABLEB\";\n"+

-            "DROP TABLE \"TABLEB\";\n"+

-            "CREATE TABLE \"TABLEB\"\n"+

-            "(\n"+

-            "    \"COLPK\" DOUBLE NOT NULL,\n"+

-            "    \"COLFK1\" INTEGER,\n"+

-            "    \"COLFK2\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"COLPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK1\",\"COLFK2\") SELECT \"COLPK\",\"COLFK1\",\"COLFK2\" FROM \"TABLEB_\";\n"+

-            "DROP TABLE \"TABLEB_\";\n"+

+            "ALTER TABLE \"TABLEB\" ADD COLUMN \"COLFK2\" DOUBLE;\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK1\",\"COLFK2\") REFERENCES \"TableA\" (\"ColPK1\",\"ColPK2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1001,7 +945,7 @@
             "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK1\") SELECT \"COLPK\",\"COLFK1\" FROM \"TABLEB_\";\n"+

             "DROP TABLE \"TABLEB_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK1\") REFERENCES \"TableA\" (\"ColPK1\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1036,26 +980,9 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TESTINDEX\" ON \"TableA\";\n"+

-            "CREATE TABLE \"TableA_\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA_\" (\"ColPK\",\"Col1\") SELECT \"ColPK\",\"Col1\" FROM \"TableA\";\n"+

-            "DROP TABLE \"TableA\";\n"+

-            "CREATE TABLE \"TableA\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK\",\"Col1\",\"Col2\") SELECT \"ColPK\",\"Col1\",\"Col2\" FROM \"TableA_\";\n"+

-            "DROP TABLE \"TableA_\";\n"+

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col2\" VARCHAR(64);\n"+

             "CREATE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\",\"Col2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1107,7 +1034,7 @@
             "INSERT INTO \"TableA\" (\"ColPK\",\"Col1\") SELECT \"ColPK\",\"Col1\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "CREATE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1142,26 +1069,9 @@
 

         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TESTINDEX\" ON \"TableA\";\n"+

-            "CREATE TABLE \"TableA_\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA_\" (\"ColPK\",\"Col1\") SELECT \"ColPK\",\"Col1\" FROM \"TableA\";\n"+

-            "DROP TABLE \"TableA\";\n"+

-            "CREATE TABLE \"TableA\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK\",\"Col1\",\"Col2\") SELECT \"ColPK\",\"Col1\",\"Col2\" FROM \"TableA_\";\n"+

-            "DROP TABLE \"TableA_\";\n"+

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col2\" VARCHAR(64);\n"+

             "CREATE UNIQUE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\",\"Col2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1213,7 +1123,7 @@
             "INSERT INTO \"TableA\" (\"ColPK\",\"Col1\") SELECT \"ColPK\",\"Col1\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "CREATE UNIQUE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

 

@@ -1247,26 +1157,8 @@
             "</database>";

 

         assertEqualsIgnoringWhitespaces(

-            "CREATE TABLE \"TableA_\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TableA_\" (\"ColPK\",\"Col1\") SELECT \"ColPK\",\"Col1\" FROM \"TableA\";\n"+

-            "DROP TABLE \"TableA\";\n"+

-            "CREATE TABLE \"TableA\"\n"+

-            "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

-            "    \"Col1\" DOUBLE,\n"+

-            "    \"Col2\" VARCHAR(64),\n"+

-            "    PRIMARY KEY (\"ColPK\")\n"+

-            ");\n"+

-            "CREATE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK\",\"Col1\",\"Col2\") SELECT \"ColPK\",\"Col1\",\"Col2\" FROM \"TableA_\";\n"+

-            "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col2\" VARCHAR(64);\n",

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1305,29 +1197,12 @@
             "</database>";

 

         assertEqualsIgnoringWhitespaces(

-            "ALTER TABLE \"TABLEB\" DROP CONSTRAINT \"TESTFK\";\n"+

-            "CREATE TABLE \"TABLEB_\"\n"+

-            "(\n"+

-            "    \"COLPK\" INTEGER NOT NULL,\n"+

-            "    \"COLFK\" INTEGER,\n"+

-            "    \"COL\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"COLPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TABLEB_\" (\"COLPK\",\"COLFK\") SELECT \"COLPK\",\"COLFK\" FROM \"TABLEB\";\n"+

-            "DROP TABLE \"TABLEB\";\n"+

-            "CREATE TABLE \"TABLEB\"\n"+

-            "(\n"+

-            "    \"COLPK\" INTEGER NOT NULL,\n"+

-            "    \"COLFK\" INTEGER,\n"+

-            "    \"COL\" DOUBLE,\n"+

-            "    PRIMARY KEY (\"COLPK\")\n"+

-            ");\n"+

-            "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK\",\"COL\") SELECT \"COLPK\",\"COLFK\",\"COL\" FROM \"TABLEB_\";\n"+

-            "DROP TABLE \"TABLEB_\";\n"+

-            "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            "ALTER TABLE \"TABLEB\" ADD COLUMN \"COL\" DOUBLE;\n",

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

+    // TODO: insert column (not add) into table (also with index/foreign key)

+

     /**

      * Tests the addition of a column to a table that is referenced by a foreign key.

      */

@@ -1364,25 +1239,65 @@
             "</database>";

 

         assertEqualsIgnoringWhitespaces(

+            "ALTER TABLE \"TableA\" ADD COLUMN \"Col\" DOUBLE;\n",

+            getAlterModelSQL(model1Xml, model2Xml));

+    }

+

+    /**

+     * Tests the addition of a column to a table that is referenced by a foreign key.

+     */

+    public void testInsertColumnToTableReferencedByForeignKey() throws IOException

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableA'>\n" +

+            "      <reference local='COLFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='Col' type='DOUBLE'/>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableA'>\n" +

+            "      <reference local='COLFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        assertEqualsIgnoringWhitespaces(

             "ALTER TABLE \"TABLEB\" DROP CONSTRAINT \"TESTFK\";\n"+

             "CREATE TABLE \"TableA_\"\n"+

             "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

             "    \"Col\" DOUBLE,\n"+

+            "    \"ColPK\" INTEGER NOT NULL,\n"+

             "    PRIMARY KEY (\"ColPK\")\n"+

             ");\n"+

             "INSERT INTO \"TableA_\" (\"ColPK\") SELECT \"ColPK\" FROM \"TableA\";\n"+

             "DROP TABLE \"TableA\";\n"+

             "CREATE TABLE \"TableA\"\n"+

             "(\n"+

-            "    \"ColPK\" INTEGER NOT NULL,\n"+

             "    \"Col\" DOUBLE,\n"+

+            "    \"ColPK\" INTEGER NOT NULL,\n"+

             "    PRIMARY KEY (\"ColPK\")\n"+

             ");\n"+

-            "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

+            "INSERT INTO \"TableA\" (\"Col\",\"ColPK\") SELECT \"Col\",\"ColPK\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1424,7 +1339,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1466,7 +1381,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1527,7 +1442,7 @@
             "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK2\",\"COLFK1\") REFERENCES \"TableA\" (\"ColPK1\",\"ColPK2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1588,7 +1503,7 @@
             "INSERT INTO \"TableA\" (\"ColPK1\",\"ColPK2\") SELECT \"ColPK1\",\"ColPK2\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK2\") REFERENCES \"TableA\" (\"ColPK1\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1625,7 +1540,7 @@
         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TESTINDEX\" ON \"TableA\";\n"+

             "CREATE UNIQUE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\",\"Col2\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1662,7 +1577,7 @@
         assertEqualsIgnoringWhitespaces(

             "DROP INDEX \"TESTINDEX\" ON \"TableA\";\n"+

             "CREATE INDEX \"TESTINDEX\" ON \"TableA\" (\"Col1\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1717,7 +1632,7 @@
             "INSERT INTO \"TableA\" (\"ColPK\") SELECT \"ColPK\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1759,7 +1674,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1801,7 +1716,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1850,7 +1765,7 @@
             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1920,7 +1835,7 @@
             "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK\") SELECT \"COLPK\",\"COLFK\" FROM \"TABLEB_\";\n"+

             "DROP TABLE \"TABLEB_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -1962,7 +1877,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2004,7 +1919,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2053,7 +1968,7 @@
             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2123,7 +2038,7 @@
             "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK\") SELECT \"COLPK\",\"COLFK\" FROM \"TABLEB_\";\n"+

             "DROP TABLE \"TABLEB_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2165,7 +2080,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2207,7 +2122,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2256,7 +2171,7 @@
             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2326,7 +2241,7 @@
             "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK\") SELECT \"COLPK\",\"COLFK\" FROM \"TABLEB_\";\n"+

             "DROP TABLE \"TABLEB_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2368,7 +2283,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2410,7 +2325,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2459,7 +2374,7 @@
             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2514,7 +2429,7 @@
             "INSERT INTO \"TableA\" (\"ColPK\") SELECT \"ColPK\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2556,7 +2471,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2598,7 +2513,7 @@
             ");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2647,7 +2562,7 @@
             "CREATE INDEX \"TestIndex\" ON \"TableA\" (\"Col\");\n"+

             "INSERT INTO \"TableA\" (\"ColPK\",\"Col\") SELECT \"ColPK\",\"Col\" FROM \"TableA_\";\n"+

             "DROP TABLE \"TableA_\";\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 

     /**

@@ -2718,6 +2633,6 @@
             "INSERT INTO \"TABLEB\" (\"COLPK\",\"COLFK\") SELECT \"COLPK\",\"COLFK\" FROM \"TABLEB_\";\n"+

             "DROP TABLE \"TABLEB_\";\n"+

             "ALTER TABLE \"TABLEB\" ADD CONSTRAINT \"TESTFK\" FOREIGN KEY (\"COLFK\") REFERENCES \"TableA\" (\"ColPK\");\n",

-            getAlterDatabaseSQL(model1Xml, model2Xml));

+            getAlterModelSQL(model1Xml, model2Xml));

     }

 }

diff --git a/src/test/org/apache/ddlutils/alteration/TestComparisonBase.java b/src/test/org/apache/ddlutils/alteration/TestComparisonBase.java
new file mode 100644
index 0000000..0fc0e8e
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestComparisonBase.java
@@ -0,0 +1,177 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+

+import org.apache.ddlutils.Platform;

+import org.apache.ddlutils.PlatformInfo;

+import org.apache.ddlutils.TestBase;

+import org.apache.ddlutils.model.Column;

+import org.apache.ddlutils.model.ForeignKey;

+import org.apache.ddlutils.model.Index;

+import org.apache.ddlutils.model.Table;

+import org.apache.ddlutils.platform.TestPlatform;

+

+/**

+ * Base class for model comparison tests.

+ * 

+ * @version $Revision: $

+ */

+public abstract class TestComparisonBase extends TestBase

+{

+    /**

+     * Creates a new platform object.

+     *

+     * @param delimitedIdentifierModeOn Whether delimited identifiers shall be used

+     * @return The platform object

+     */

+    protected Platform getPlatform(boolean delimitedIdentifierModeOn)

+    {

+        TestPlatform platform = new TestPlatform() {

+            protected TableDefinitionChangesPredicate getTableDefinitionChangesPredicate()

+            {

+                return null;

+            }

+        };

+

+        PlatformInfo platformInfo = platform.getPlatformInfo();

+

+        platform.setDelimitedIdentifierModeOn(delimitedIdentifierModeOn);

+        platformInfo.setHasSize(Types.DECIMAL, true);

+        platformInfo.setHasSize(Types.NUMERIC, true);

+        platformInfo.setHasSize(Types.CHAR, true);

+        platformInfo.setHasSize(Types.VARCHAR, true);

+

+        return platform;

+    }

+

+    /**

+     * Asserts the given table.

+     * 

+     * @param name        The expected name

+     * @param description The expected description

+     * @param columnCount The expected number of columns

+     * @param fkCount     The expected number of foreign keys

+     * @param indexCount  The expected number of indexes

+     * @param table       The table to assert

+     */

+    protected void assertTable(String name, String description, int columnCount, int fkCount, int indexCount, Table table)

+    {

+        assertEquals(name,

+                     table.getName());

+        assertEquals(description,

+                     table.getDescription());

+        assertEquals(columnCount,

+                     table.getColumnCount());

+        assertEquals(fkCount,

+                     table.getForeignKeyCount());

+        assertEquals(indexCount,

+                     table.getIndexCount());

+    }

+

+    /**

+     * Asserts the given column.

+     * 

+     * @param name            The expected name

+     * @param typeCode        The expected type code

+     * @param sizeSpec        The expected size

+     * @param defaultValue    The expected default value

+     * @param isPrimaryKey    The expected primary key status

+     * @param isRequired      The expected required status

+     * @param isAutoIncrement The expected auto increment status

+     * @param column          The column to assert

+     */

+    protected void assertColumn(String  name,

+                                int     typeCode,

+                                String  sizeSpec,

+                                String  defaultValue,

+                                boolean isPrimaryKey,

+                                boolean isRequired,

+                                boolean isAutoIncrement,

+                                Column  column)

+    {

+        assertEquals(name,

+                     column.getName());

+        assertEquals(typeCode,

+                     column.getTypeCode());

+        assertEquals(sizeSpec,

+                     column.getSize());

+        assertEquals(defaultValue,

+                     column.getDefaultValue());

+        assertEquals(isPrimaryKey,

+                     column.isPrimaryKey());

+        assertEquals(isRequired,

+                     column.isRequired());

+        assertEquals(isAutoIncrement,

+                     column.isAutoIncrement());

+    }

+

+    /**

+     * Asserts the given index.

+     * 

+     * @param name         The expected name

+     * @param isUnique     Whether the index is expected to be a unique index

+     * @param indexColumns The names of the columns expected to be in the index

+     * @param index        The index to assert

+     */

+    protected void assertIndex(String name, boolean isUnique, String[] indexColumns, Index index)

+    {

+        assertEquals(name,

+                     index.getName());

+        assertEquals(isUnique,

+                     index.isUnique());

+        assertEquals(indexColumns.length,

+                     index.getColumnCount());

+        for (int idx = 0; idx < indexColumns.length; idx++)

+        {

+            assertEquals(indexColumns[idx],

+                         index.getColumn(idx).getName());

+            assertEquals(indexColumns[idx],

+                         index.getColumn(idx).getColumn().getName());

+        }

+    }

+

+    /**

+     * Asserts the given foreign key.

+     * 

+     * @param name               The expected name

+     * @param targetTableName    The name of the expected target table

+     * @param localColumnNames   The names of the expected local columns

+     * @param foreignColumnNames The names of the expected foreign columns

+     * @param fk                 The foreign key to assert

+     */

+    protected void assertForeignKey(String name, String targetTableName, String[] localColumnNames, String[] foreignColumnNames, ForeignKey fk)

+    {

+        assertEquals(name,

+                     fk.getName());

+        assertEquals(targetTableName,

+                     fk.getForeignTable().getName());

+        assertEquals(localColumnNames.length,

+                     fk.getReferenceCount());

+        for (int idx = 0; idx < localColumnNames.length; idx++)

+        {

+            assertEquals(localColumnNames[idx],

+                         fk.getReference(idx).getLocalColumnName());

+            assertEquals(foreignColumnNames[idx],

+                         fk.getReference(idx).getForeignColumnName());

+        }

+    }

+}

diff --git a/src/test/org/apache/ddlutils/alteration/TestForeignKeyComparison.java b/src/test/org/apache/ddlutils/alteration/TestForeignKeyComparison.java
new file mode 100644
index 0000000..fb3c9aa
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestForeignKeyComparison.java
@@ -0,0 +1,1541 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+import java.util.List;

+

+import org.apache.ddlutils.model.Database;

+

+/**

+ * Tests the model comparison.

+ * 

+ * TODO: need tests with foreign key without a name

+ * 

+ * @version $Revision: $

+ */

+public class TestForeignKeyComparison extends TestComparisonBase

+{

+    /**

+     * Tests the addition of a single-column foreign key.

+     */

+    public void testAddColumnAndForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        AddColumnChange     colChange = (AddColumnChange)changes.get(0);

+        AddForeignKeyChange fkChange  = (AddForeignKeyChange)changes.get(1);

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("COLFK", Types.INTEGER, null, null, false, false, false,

+                     colChange.getNewColumn());

+        assertEquals("ColPK",

+                     colChange.getPreviousColumn());

+        assertNull(colChange.getNextColumn());

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "COLFK" }, new String[] { "ColPK" },

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a single-column foreign key.

+     */

+    public void testAddColumnAndForeignKeyToIt()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        AddColumnChange     colChange = (AddColumnChange)changes.get(0);

+        AddPrimaryKeyChange pkChange  = (AddPrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange fkChange  = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableB",

+                     colChange.getChangedTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, false, true, false,

+                     colChange.getNewColumn());

+        assertNull(colChange.getPreviousColumn());

+        assertEquals("Col",

+                     colChange.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(1,

+                     pkChange.getPrimaryKeyColumns().length);

+        assertEquals("COLPK",

+                     pkChange.getPrimaryKeyColumns()[0]);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "COLFK" }, new String[] { "COLPK" },

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a multi-column foreign key.

+     */

+    public void testAddColumnsAndForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        AddColumnChange     colChange1 = (AddColumnChange)changes.get(0);

+        AddColumnChange     colChange2 = (AddColumnChange)changes.get(1);

+        AddColumnChange     colChange3 = (AddColumnChange)changes.get(2);

+        AddForeignKeyChange fkChange   = (AddForeignKeyChange)changes.get(3);

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertColumn("COLFK1", Types.INTEGER, null, null, false, false, false,

+                     colChange1.getNewColumn());

+        assertEquals("ColPK",

+                     colChange1.getPreviousColumn());

+        assertNull(colChange1.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("COLFK2", Types.DOUBLE, null, null, false, false, false,

+                     colChange2.getNewColumn());

+        assertEquals("COLFK1",

+                     colChange2.getPreviousColumn());

+        assertNull(colChange2.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange3.getChangedTable());

+        assertColumn("COLFK3", Types.VARCHAR, "32", null, false, false, false,

+                     colChange3.getNewColumn());

+        assertEquals("COLFK2",

+                     colChange3.getPreviousColumn());

+        assertNull(colChange3.getNextColumn());

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "COLFK2", "COLFK1", "COLFK3" }, new String[] { "ColPK1", "ColPK2", "ColPK3" },

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a multi-column foreign key.

+     */

+    public void testAddColumnsAndForeignKeyToThem()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        AddColumnChange     colChange1 = (AddColumnChange)changes.get(0);

+        AddColumnChange     colChange2 = (AddColumnChange)changes.get(1);

+        PrimaryKeyChange    pkChange   = (PrimaryKeyChange)changes.get(2);

+        AddForeignKeyChange fkChange   = (AddForeignKeyChange)changes.get(3);

+

+        assertEquals("TableB",

+                     colChange1.getChangedTable());

+        assertColumn("COLPK1", Types.DOUBLE, null, null, false, true, false,

+                     colChange1.getNewColumn());

+        assertNull(colChange1.getPreviousColumn());

+        assertEquals("ColPK3",

+                     colChange1.getNextColumn());

+

+        assertEquals("TableB",

+                     colChange2.getChangedTable());

+        assertColumn("COLPK2", Types.INTEGER, null, null, false, true, false,

+                     colChange2.getNewColumn());

+        assertEquals("COLPK1",

+                     colChange2.getPreviousColumn());

+        assertEquals("ColPK3",

+                     colChange2.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("ColPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1", "ColFK3" }, new String[] { "COLPK1", "COLPK2", "ColPK3" },

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a multi-column foreign key.

+     */

+    public void testAddColumnsAndForeignKeyBetweenThem()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(6,

+                     changes.size());

+

+        AddColumnChange     colChange1 = (AddColumnChange)changes.get(0);

+        AddColumnChange     colChange2 = (AddColumnChange)changes.get(1);

+        AddColumnChange     colChange3 = (AddColumnChange)changes.get(2);

+        AddColumnChange     colChange4 = (AddColumnChange)changes.get(3);

+        PrimaryKeyChange    pkChange   = (PrimaryKeyChange)changes.get(4);

+        AddForeignKeyChange fkChange   = (AddForeignKeyChange)changes.get(5);

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertColumn("COLFK1", Types.INTEGER, null, null, false, false, false,

+                     colChange1.getNewColumn());

+        assertEquals("ColPK",

+                     colChange1.getPreviousColumn());

+        assertEquals("ColFK2",

+                     colChange1.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("COLFK3", Types.VARCHAR, "32", null, false, false, false,

+                     colChange2.getNewColumn());

+        assertEquals("ColFK2",

+                     colChange2.getPreviousColumn());

+        assertNull(colChange2.getNextColumn());

+

+        assertEquals("TableB",

+                     colChange3.getChangedTable());

+        assertColumn("COLPK1", Types.DOUBLE, null, null, false, true, false,

+                     colChange3.getNewColumn());

+        assertNull(colChange3.getPreviousColumn());

+        assertEquals("ColPK3",

+                     colChange3.getNextColumn());

+

+        assertEquals("TableB",

+                     colChange4.getChangedTable());

+        assertColumn("COLPK2", Types.INTEGER, null, null, false, true, false,

+                     colChange4.getNewColumn());

+        assertEquals("COLPK1",

+                     colChange4.getPreviousColumn());

+        assertEquals("ColPK3",

+                     colChange4.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("ColPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "COLFK1", "COLFK3" }, new String[] { "COLPK1", "COLPK2", "ColPK3" },

+                         fkChange.getNewForeignKey());

+    }

+    

+    /**

+     * Tests the addition of a single reference foreign key.

+     */

+    public void testAddSingleReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddForeignKeyChange change = (AddForeignKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK" }, new String[] { "ColPK" },

+                         change.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a multi-reference foreign key.

+     */

+    public void testAddMultiReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddForeignKeyChange fkChange = (AddForeignKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1", "ColFK3" }, new String[] { "ColPK1", "ColPK2", "COLPK3" },

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a column to a multi-reference foreign key.

+     */

+    public void testAddLocalColumnToMultiReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='ColPK2'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        AddColumnChange        colChange = (AddColumnChange)changes.get(1);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(2);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(3);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("COLFK2", Types.DOUBLE, null, null, false, false, false,

+                     colChange.getNewColumn());

+        assertEquals("ColFK1",

+                     colChange.getPreviousColumn());

+        assertEquals("ColFK3",

+                     colChange.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "COLFK2", "ColFK1", "ColFK3" }, new String[] { "ColPK1", "ColPK2", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a column to a multi-reference foreign key.

+     */

+    public void testAddForeignColumnToMultiReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='ColPK1'/>\n" +

+            "      <reference local='ColFK1' foreign='ColPK2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        AddColumnChange        colChange = (AddColumnChange)changes.get(1);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(2);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(3);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     colChange.getChangedTable());

+        assertColumn("COLPK3", Types.VARCHAR, "32", null, false, true, false,

+                     colChange.getNewColumn());

+        assertEquals("ColPK2",

+                     colChange.getPreviousColumn());

+        assertNull(colChange.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1", "ColFK3" }, new String[] { "ColPK1", "ColPK2", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of columns to a single-reference foreign key.

+     */

+    public void testAddColumnsToSingleReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='ColPK1'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(7,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1  = (RemoveForeignKeyChange)changes.get(0);

+        AddColumnChange        colChange1 = (AddColumnChange)changes.get(1);

+        AddColumnChange        colChange2 = (AddColumnChange)changes.get(2);

+        AddColumnChange        colChange3 = (AddColumnChange)changes.get(3);

+        AddColumnChange        colChange4 = (AddColumnChange)changes.get(4);

+        PrimaryKeyChange       pkChange   = (PrimaryKeyChange)changes.get(5);

+        AddForeignKeyChange    fkChange2  = (AddForeignKeyChange)changes.get(6);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertColumn("COLFK1", Types.INTEGER, null, null, false, false, false,

+                     colChange1.getNewColumn());

+        assertEquals("ColPK",

+                     colChange1.getPreviousColumn());

+        assertEquals("ColFK2",

+                     colChange1.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("COLFK3", Types.VARCHAR, "32", null, false, false, false,

+                     colChange2.getNewColumn());

+        assertEquals("ColFK2",

+                     colChange2.getPreviousColumn());

+        assertNull(colChange2.getNextColumn());

+

+        assertEquals("TableB",

+                     colChange3.getChangedTable());

+        assertColumn("COLPK2", Types.INTEGER, null, null, false, true, false,

+                     colChange3.getNewColumn());

+        assertEquals("ColPK1",

+                     colChange3.getPreviousColumn());

+        assertNull(colChange3.getNextColumn());

+

+        assertEquals("TableB",

+                     colChange4.getChangedTable());

+        assertColumn("COLPK3", Types.VARCHAR, "32", null, false, true, false,

+                     colChange4.getNewColumn());

+        assertEquals("COLPK2",

+                     colChange4.getPreviousColumn());

+        assertNull(colChange4.getNextColumn());

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "COLFK1", "COLFK3" }, new String[] { "ColPK1", "COLPK2", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of a reference to a multi-reference foreign key.

+     */

+    public void testAddReferenceToMultiReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1", "ColFK3" }, new String[] { "COLPK1", "COLPK2", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of references to a single-reference foreign key.

+     */

+    public void testAddReferencesToSingleReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1", "ColFK3" }, new String[] { "COLPK1", "COLPK2", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests that the order of the references in a foreign key is not important.

+     */

+    public void testForeignKeyReferenceOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

+            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

+            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertTrue(changes.isEmpty());

+    }

+

+    /**

+     * Tests adding a reference to a foreign key and changing the order of references.

+     */

+    public void testAddReferenceToForeignKeyAndChangeOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("COLPK3",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK1", "ColFK2", "ColFK3" }, new String[] { "COLPK2", "COLPK1", "COLPK3" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests removing a reference from a foreign key.

+     */

+    public void testRemoveReferenceFromForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(2,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK1", "ColFK2" }, new String[] { "COLPK2", "COLPK1" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests removing a reference from a foreign key and changing the order of references.

+     */

+    public void testRemoveReferenceFromForeignKeyAndChangeOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1 = (RemoveForeignKeyChange)changes.get(0);

+        PrimaryKeyChange       pkChange  = (PrimaryKeyChange)changes.get(1);

+        AddForeignKeyChange    fkChange2 = (AddForeignKeyChange)changes.get(2);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableB",

+                     pkChange.getChangedTable());

+        assertEquals(2,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("COLPK1",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("COLPK2",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK2", "ColFK1" }, new String[] { "COLPK1", "COLPK2" },

+                         fkChange2.getNewForeignKey());

+    }

+

+    // TODO: drop column  from reference PK

+

+    /**

+     * Tests dropping columns used in a foreign key.

+     */

+    public void testDropColumnsFromForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK1' foreign='COLPK2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(5,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange1  = (RemoveForeignKeyChange)changes.get(0);

+        RemoveColumnChange     colChange1 = (RemoveColumnChange)changes.get(1);

+        RemoveColumnChange     colChange2 = (RemoveColumnChange)changes.get(2);

+        RemoveColumnChange     colChange3 = (RemoveColumnChange)changes.get(3);

+        AddForeignKeyChange    fkChange2  = (AddForeignKeyChange)changes.get(4);

+

+        assertEquals("TableA",

+                     fkChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange1.findChangedForeignKey(model1, false));

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertEquals("ColFK3",

+                     colChange1.getChangedColumn());

+

+        assertEquals("TableB",

+                     colChange2.getChangedTable());

+        assertEquals("COLPK1",

+                     colChange2.getChangedColumn());

+

+        assertEquals("TableB",

+                     colChange3.getChangedTable());

+        assertEquals("COLPK3",

+                     colChange3.getChangedColumn());

+

+        assertEquals("TableA",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFK", "TableB", new String[] { "ColFK1" }, new String[] { "COLPK2" },

+                         fkChange2.getNewForeignKey());

+    }

+    

+    /**

+     * Tests the removal of a foreign key.

+     */

+    public void testDropSingleReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableA'>\n" +

+            "      <reference local='ColFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveForeignKeyChange change = (RemoveForeignKeyChange)changes.get(0);

+

+        assertEquals("TableB",

+                     change.getChangedTable());

+        assertEquals(model1.findTable("TableB").getForeignKey(0),

+                     change.findChangedForeignKey(model1, false));

+    }

+

+

+    /**

+     * Tests dropping a multi-reference foreign key.

+     */

+    public void testDropMultiReferenceForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='DOUBLE'/>\n" +

+            "    <column name='ColFK3' type='VARCHAR' size='32'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='COLPK2'/>\n" +

+            "      <reference local='ColFK2' foreign='COLPK1'/>\n" +

+            "      <reference local='ColFK3' foreign='COLPK3'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK1' type='INTEGER'/>\n" +

+            "    <column name='COLFK2' type='DOUBLE'/>\n" +

+            "    <column name='COLFK3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK1' type='DOUBLE' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLPK3' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange = (RemoveForeignKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange.findChangedForeignKey(model1, false));

+    }

+

+    /**

+     * Tests the addition and removal of a foreign key.

+     */

+    public void testAddAndDropForeignKey1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableA'>\n" +

+            "      <reference local='ColFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFK' foreignTable='TableA'>\n" +

+            "      <reference local='ColFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveForeignKeyChange change1 = (RemoveForeignKeyChange)changes.get(0);

+        AddForeignKeyChange    change2 = (AddForeignKeyChange)changes.get(1);

+

+        assertEquals("TableB",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableB").getForeignKey(0),

+                     change1.findChangedForeignKey(model1, true));

+

+        assertEquals("TableB",

+                     change2.getChangedTable());

+        assertForeignKey("TESTFK", "TableA", new String[] { "ColFK" }, new String[] { "ColPK" },

+                         change2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the recreation of a foreign key because of a change of the references.

+     */

+    public void testAddAndDropForeignKey2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

+            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColFK1' type='INTEGER'/>\n" +

+            "    <column name='ColFK2' type='INTEGER'/>\n" +

+            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

+            "      <reference local='ColFK1' foreign='ColPK2'/>\n" +

+            "      <reference local='ColFK2' foreign='ColPK1'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveForeignKeyChange change1 = (RemoveForeignKeyChange)changes.get(0);

+        AddForeignKeyChange    change2 = (AddForeignKeyChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     change1.findChangedForeignKey(model1, true));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertForeignKey("TestFK", "TableB", new String[] { "ColFK1", "ColFK2" }, new String[] { "ColPK2", "ColPK1" },

+                         change2.getNewForeignKey());

+    }

+

+    // TODO: foreign key change to different table

+    // TODO: foreign key change to different columns (?)

+}

diff --git a/src/test/org/apache/ddlutils/alteration/TestIndexComparison.java b/src/test/org/apache/ddlutils/alteration/TestIndexComparison.java
new file mode 100644
index 0000000..dd72388
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestIndexComparison.java
@@ -0,0 +1,1065 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+import java.util.List;

+

+import org.apache.ddlutils.model.Database;

+

+/**

+ * Tests the model comparison.

+ * 

+ * TODO: need tests with indexes without a name

+ * 

+ * @version $Revision: $

+ */

+public class TestIndexComparison extends TestComparisonBase

+{

+    /**

+     * Tests the addition of an index with one column.

+     */

+    public void testAddSingleColumnIndex1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddIndexChange change = (AddIndexChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertIndex("TESTINDEX", false, new String[] { "COL" },

+                    change.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of an index with one column.

+     */

+    public void testAddSingleColumnIndex2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        AddColumnChange colChange   = (AddColumnChange)changes.get(0);

+        AddIndexChange  indexChange = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("COL", Types.INTEGER, null, null, false, false, false,

+                     colChange.getNewColumn());

+        assertEquals("ColPK",

+                     colChange.getPreviousColumn());

+        assertNull(colChange.getNextColumn());

+

+        assertEquals("TableA",

+                     indexChange.getChangedTable());

+        assertIndex("TESTINDEX", false, new String[] { "COL" },

+                     indexChange.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of an index with multiple columns.

+     */

+    public void testAddMultiColumnIndex1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddIndexChange change = (AddIndexChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL3", "COL1", "COL2" },

+                    change.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of an index with multiple columns.

+     */

+    public void testAddMultiColumnIndex2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        AddColumnChange colChange1  = (AddColumnChange)changes.get(0);

+        AddColumnChange colChange2  = (AddColumnChange)changes.get(1);

+        AddColumnChange colChange3  = (AddColumnChange)changes.get(2);

+        AddIndexChange  indexChange = (AddIndexChange)changes.get(3);

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertColumn("COL1", Types.INTEGER, null, null, false, false, false,

+                     colChange1.getNewColumn());

+        assertEquals("ColPK",

+                     colChange1.getPreviousColumn());

+        assertNull(colChange1.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("COL2", Types.DOUBLE, null, null, false, false, false,

+                     colChange2.getNewColumn());

+        assertEquals("COL1",

+                     colChange2.getPreviousColumn());

+        assertNull(colChange2.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange3.getChangedTable());

+        assertColumn("COL3", Types.VARCHAR, "32", null, false, false, false,

+                     colChange3.getNewColumn());

+        assertEquals("COL2",

+                     colChange3.getPreviousColumn());

+        assertNull(colChange3.getNextColumn());

+

+        assertEquals("TableA",

+                     indexChange.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL3", "COL1", "COL2" },

+                    indexChange.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of a column into an existing index with multiple columns.

+     */

+    public void testAddNewColumnToMultiColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveIndexChange indexChange1 = (RemoveIndexChange)changes.get(0);

+        AddColumnChange   colChange    = (AddColumnChange)changes.get(1);

+        AddIndexChange    indexChange2 = (AddIndexChange)changes.get(2);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("COL2", Types.DOUBLE, null, null, false, false, false,

+                     colChange.getNewColumn());

+        assertEquals("COL1",

+                     colChange.getPreviousColumn());

+        assertEquals("COL3",

+                     colChange.getNextColumn());

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL3", "COL1", "COL2" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of columns to an existing index with a single column.

+     */

+    public void testAddNewColumnsToSingleColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        RemoveIndexChange indexChange1 = (RemoveIndexChange)changes.get(0);

+        AddColumnChange   colChange1   = (AddColumnChange)changes.get(1);

+        AddColumnChange   colChange2   = (AddColumnChange)changes.get(2);

+        AddIndexChange    indexChange2 = (AddIndexChange)changes.get(3);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertColumn("COL1", Types.INTEGER, null, null, false, false, false,

+                     colChange1.getNewColumn());

+        assertEquals("ColPK",

+                     colChange1.getPreviousColumn());

+        assertEquals("COL3",

+                     colChange1.getNextColumn());

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("COL2", Types.DOUBLE, null, null, false, false, false,

+                     colChange2.getNewColumn());

+        assertEquals("COL1",

+                     colChange2.getPreviousColumn());

+        assertEquals("COL3",

+                     colChange2.getNextColumn());

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL1", "COL3", "COL2" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of a column to an index with multiple columns.

+     */

+    public void testAddColumnToMultiColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL3'/>\n" +

+            "      <index-column name='COL2'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL3'/>\n" +

+            "      <index-column name='COL1'/>\n" +

+            "      <index-column name='COL2'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange indexChange1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    indexChange2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", false, new String[] { "COL3", "COL1", "COL2" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of columns to an index with a single column.

+     */

+    public void testAddColumnsToSingleColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange indexChange1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    indexChange2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL3", "COL1", "COL2" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the addition and removal of an index because of the change of column order.

+     */

+    public void testChangeIndexColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <unique name='TestIndex'>\n" +

+            "      <unique-column name='Col1'/>\n" +

+            "      <unique-column name='Col2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <unique name='TestIndex'>\n" +

+            "      <unique-column name='Col2'/>\n" +

+            "      <unique-column name='Col1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertIndex("TestIndex", true, new String[] { "Col2", "Col1" }, 

+                     change2.getNewIndex());

+    }

+    

+    /**

+     * Tests the recreation of an index because of the addition of an index column.

+     */

+    public void testAddIndexColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col1'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col1'/>\n" +

+            "      <index-column name='Col2'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertIndex("TestIndex", false, new String[] { "Col1", "Col2" }, 

+                     change2.getNewIndex());

+    }

+   

+    /**

+     * Tests the addition and removal of an index because of the removal of an index column.

+     */

+    public void testRemoveIndexColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col1'/>\n" +

+            "      <index-column name='Col2'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col1'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertIndex("TestIndex", false, new String[] { "Col1" }, 

+                    change2.getNewIndex());

+    }

+

+    /**

+     * Tests changing the type of an index.

+     */

+    public void testChangeIndexType()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col1'/>\n" +

+            "      <index-column name='Col2'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER'/>\n" +

+            "    <column name='Col2' type='DOUBLE'/>\n" +

+            "    <unique name='TestIndex'>\n" +

+            "      <unique-column name='Col1'/>\n" +

+            "      <unique-column name='Col2'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertIndex("TestIndex", true, new String[] { "Col1", "Col2" }, 

+                    change2.getNewIndex());

+    }

+

+    /**

+     * Tests the removal of a column that is the single column in an index.

+     */

+    public void testDropColumnFromSingleColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL1'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange  indexChange = (RemoveIndexChange)changes.get(0);

+        RemoveColumnChange colChange   = (RemoveColumnChange)changes.get(1);

+

+        assertEquals("TableA",

+                     indexChange.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals("COL1",

+                     colChange.getChangedColumn());

+    }

+

+    /**

+     * Tests the removal of a column that is part of an index.

+     */

+    public void testDropColumnFromMultiColumnIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveIndexChange  indexChange1 = (RemoveIndexChange)changes.get(0);

+        RemoveColumnChange colChange    = (RemoveColumnChange)changes.get(1);

+        AddIndexChange     indexChange2 = (AddIndexChange)changes.get(2);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals("COL2",

+                     colChange.getChangedColumn());

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL3", "COL1" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the addition of a column and changing the order of the columns in an index.

+     */

+    public void testAddColumnAndChangeIndexColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveIndexChange indexChange1 = (RemoveIndexChange)changes.get(0);

+        AddColumnChange   colChange    = (AddColumnChange)changes.get(1);

+        AddIndexChange    indexChange2 = (AddIndexChange)changes.get(2);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("COL2", Types.DOUBLE, null, null, false, false, false,

+                     colChange.getNewColumn());

+        assertEquals("COL1",

+                     colChange.getPreviousColumn());

+        assertEquals("COL3",

+                     colChange.getNextColumn());

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL1", "COL2", "COL3" },

+                    indexChange2.getNewIndex());

+    }

+

+    /**

+     * Tests the removal of a column and changing the order of the columns in an index.

+     */

+    public void testDropColumnAndChangeIndexColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "      <unique-column name='COL2'/>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <unique name='TESTINDEX'>\n" +

+            "      <unique-column name='COL1'/>\n" +

+            "      <unique-column name='COL3'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveIndexChange  indexChange1 = (RemoveIndexChange)changes.get(0);

+        RemoveColumnChange colChange    = (RemoveColumnChange)changes.get(1);

+        AddIndexChange     indexChange2 = (AddIndexChange)changes.get(2);

+

+        assertEquals("TableA",

+                     indexChange1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals("COL2",

+                     colChange.getChangedColumn());

+

+        assertEquals("TableA",

+                     indexChange2.getChangedTable());

+        assertIndex("TESTINDEX", true, new String[] { "COL1", "COL3" },

+                    indexChange2.getNewIndex());

+    }

+    

+    /**

+     * Tests the removal of an index.

+     */

+    public void testDropIndex1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "    <unique name='TestIndex'>\n" +

+            "      <unique-column name='Col'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveIndexChange change = (RemoveIndexChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change.findChangedIndex(model1, true));

+    }

+

+    /**

+     * Tests the removal of an index.

+     */

+    public void testDropIndex2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "    <index name='TESTINDEX'>\n" +

+            "      <index-column name='COL3'/>\n" +

+            "      <index-column name='COL2'/>\n" +

+            "      <index-column name='COL1'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL1' type='INTEGER'/>\n" +

+            "    <column name='COL2' type='DOUBLE'/>\n" +

+            "    <column name='COL3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveIndexChange indexChange = (RemoveIndexChange)changes.get(0);

+

+        assertEquals("TableA",

+                     indexChange.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     indexChange.findChangedIndex(model1, false));

+    }

+    

+    /**

+     * Tests the recreation of an index because of the change of type of the index.

+     */

+    public void testAddAndDropIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "    <unique name='TestIndex'>\n" +

+            "      <unique-column name='Col'/>\n" +

+            "    </unique>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='Col'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

+        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(model1.findTable("TableA").getIndex(0),

+                     change1.findChangedIndex(model1, false));

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertIndex("TestIndex", false, new String[] { "Col" }, 

+                     change2.getNewIndex());

+    }

+}

diff --git a/src/test/org/apache/ddlutils/alteration/TestModelComparator.java b/src/test/org/apache/ddlutils/alteration/TestModelComparator.java
deleted file mode 100644
index b48564d..0000000
--- a/src/test/org/apache/ddlutils/alteration/TestModelComparator.java
+++ /dev/null
@@ -1,1311 +0,0 @@
-package org.apache.ddlutils.alteration;

-

-/*

- * Licensed to the Apache Software Foundation (ASF) under one

- * or more contributor license agreements.  See the NOTICE file

- * distributed with this work for additional information

- * regarding copyright ownership.  The ASF licenses this file

- * to you under the Apache License, Version 2.0 (the

- * "License"); you may not use this file except in compliance

- * with the License.  You may obtain a copy of the License at

- *

- *   http://www.apache.org/licenses/LICENSE-2.0

- *

- * Unless required by applicable law or agreed to in writing,

- * software distributed under the License is distributed on an

- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

- * KIND, either express or implied.  See the License for the

- * specific language governing permissions and limitations

- * under the License.

- */

-

-import java.sql.Types;

-import java.util.List;

-

-import org.apache.ddlutils.PlatformInfo;

-import org.apache.ddlutils.TestBase;

-import org.apache.ddlutils.model.Database;

-import org.apache.ddlutils.model.Table;

-

-/**

- * Tests the model comparison.

- * 

- * @version $Revision: $

- */

-public class TestModelComparator extends TestBase

-{

-    /**

-     * Creates a new model comparator.

-     *

-     * @param caseSensitive Whether the comparison is case sensitive 

-     * @return The model comparator

-     */

-    protected ModelComparator createModelComparator(boolean caseSensitive)

-    {

-        PlatformInfo platformInfo = new PlatformInfo();

-

-        platformInfo.setHasSize(Types.DECIMAL, true);

-        platformInfo.setHasSize(Types.NUMERIC, true);

-        platformInfo.setHasSize(Types.CHAR, true);

-        platformInfo.setHasSize(Types.VARCHAR, true);

-        return new ModelComparator(platformInfo, caseSensitive);

-    }

-

-    /**

-     * Tests the addition of a table.

-     */

-    public void testAddTable()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TABLEB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        AddTableChange change = (AddTableChange)changes.get(0);

-

-        assertEquals("TABLEB",

-                     change.getNewTable().getName());

-    }

-

-    /**

-     * Tests the removal of a table.

-     */

-    public void testRemoveTable()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        RemoveTableChange change = (RemoveTableChange)changes.get(0);

-

-        assertEquals("TableA",

-                     change.getChangedTable().getName());

-    }

-

-    /**

-     * Tests the addition and removal of a table.

-     */

-    public void testAddAndRemoveTable()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        AddTableChange    change1 = (AddTableChange)changes.get(0);

-        RemoveTableChange change2 = (RemoveTableChange)changes.get(1);

-

-        assertEquals("TABLEA",

-                     change1.getNewTable().getName());

-        assertEquals("TableA",

-                     change2.getChangedTable().getName());

-    }

-

-    /**

-     * Tests the addition of a foreign key.

-     */

-    public void testAddForeignKey()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COLFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TESTFK' foreignTable='TABLEB'>\n" +

-            "      <reference local='COLFK' foreign='COLPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TABLEB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        AddForeignKeyChange change = (AddForeignKeyChange)changes.get(0);

-

-        assertEquals("TESTFK",

-                     change.getNewForeignKey().getName());

-    }

-

-    /**

-     * Tests the addition of two tables with foreign keys to each other .

-     */

-    public void testAddTablesWithForeignKeys()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COLFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TESTFKB' foreignTable='TABLEB'>\n" +

-            "      <reference local='COLFK' foreign='COLPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TABLEB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COLFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TESTFKA' foreignTable='TABLEA'>\n" +

-            "      <reference local='COLFK' foreign='COLPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(4,

-                     changes.size());

-

-        AddTableChange      tableChange1 = (AddTableChange)changes.get(0);

-        AddForeignKeyChange fkChange1    = (AddForeignKeyChange)changes.get(1);

-        AddTableChange      tableChange2 = (AddTableChange)changes.get(2);

-        AddForeignKeyChange fkChange2    = (AddForeignKeyChange)changes.get(3);

-

-        assertEquals("TABLEA",

-                     tableChange1.getNewTable().getName());

-        assertEquals("TABLEB",

-                     tableChange2.getNewTable().getName());

-        assertEquals("TESTFKB",

-                     fkChange1.getNewForeignKey().getName());

-        assertEquals("TABLEA",

-                     fkChange1.getChangedTable().getName());

-        assertEquals("TESTFKA",

-                     fkChange2.getNewForeignKey().getName());

-        assertEquals("TABLEB",

-                     fkChange2.getChangedTable().getName());

-    }

-

-    /**

-     * Tests the removal of a foreign key.

-     */

-    public void testRemoveForeignKey()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableA'>\n" +

-            "      <reference local='ColFK' foreign='ColPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TABLEB'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COLFK' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        RemoveForeignKeyChange change = (RemoveForeignKeyChange)changes.get(0);

-

-        assertEquals("TestFK",

-                     change.getChangedForeignKey().getName());

-    }

-

-    /**

-     * Tests the addition and removal of a foreign key.

-     */

-    public void testAddAndRemoveForeignKey1()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableA'>\n" +

-            "      <reference local='ColFK' foreign='ColPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK' type='INTEGER'/>\n" +

-            "    <foreign-key name='TESTFK' foreignTable='TableA'>\n" +

-            "      <reference local='ColFK' foreign='ColPK'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveForeignKeyChange change1 = (RemoveForeignKeyChange)changes.get(0);

-        AddForeignKeyChange    change2 = (AddForeignKeyChange)changes.get(1);

-

-        assertEquals("TestFK",

-                     change1.getChangedForeignKey().getName());

-        assertEquals("TESTFK",

-                     change2.getNewForeignKey().getName());

-    }

-

-    /**

-     * Tests the addition and removal of a foreign key because of a change of the references.

-     */

-    public void testAddAndRemoveForeignKey2()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK1' type='INTEGER'/>\n" +

-            "    <column name='ColFK2' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

-            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

-            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK1' type='INTEGER'/>\n" +

-            "    <column name='ColFK2' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

-            "      <reference local='ColFK1' foreign='ColPK2'/>\n" +

-            "      <reference local='ColFK2' foreign='ColPK1'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveForeignKeyChange change1 = (RemoveForeignKeyChange)changes.get(0);

-        AddForeignKeyChange    change2 = (AddForeignKeyChange)changes.get(1);

-

-        assertEquals("TestFK",

-                     change1.getChangedForeignKey().getName());

-        assertEquals("TestFK",

-                     change2.getNewForeignKey().getName());

-    }

-

-

-    /**

-     * Tests that the order of the references in a foreign key is not important.

-     */

-    public void testForeignKeyReferenceOrder()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK1' type='INTEGER'/>\n" +

-            "    <column name='ColFK2' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

-            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

-            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColFK1' type='INTEGER'/>\n" +

-            "    <column name='ColFK2' type='INTEGER'/>\n" +

-            "    <foreign-key name='TestFK' foreignTable='TableB'>\n" +

-            "      <reference local='ColFK2' foreign='ColPK2'/>\n" +

-            "      <reference local='ColFK1' foreign='ColPK1'/>\n" +

-            "    </foreign-key>\n" +

-            "  </table>\n" +

-            "  <table name='TableB'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertTrue(changes.isEmpty());

-    }

-

-    /**

-     * Tests the addition of an index.

-     */

-    public void testAddIndex()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='INTEGER'/>\n" +

-            "    <index name='TESTINDEX'>\n" +

-            "      <index-column name='COL'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        AddIndexChange change = (AddIndexChange)changes.get(0);

-

-        assertEquals("TESTINDEX",

-                     change.getNewIndex().getName());

-    }

-

-    /**

-     * Tests the removal of an index.

-     */

-    public void testRemoveIndex()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "    <unique name='TestIndex'>\n" +

-            "      <unique-column name='Col'/>\n" +

-            "    </unique>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        RemoveIndexChange change = (RemoveIndexChange)changes.get(0);

-

-        assertEquals("TestIndex",

-                     change.getChangedIndex().getName());

-    }

-

-    /**

-     * Tests the addition and removal of an index because of the change of type of the index.

-     */

-    public void testAddAndRemoveIndex()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "    <unique name='TestIndex'>\n" +

-            "      <unique-column name='Col'/>\n" +

-            "    </unique>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

-        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

-

-        assertEquals("TestIndex",

-                     change1.getChangedIndex().getName());

-        assertEquals("TestIndex",

-                     change2.getNewIndex().getName());

-    }

-

-    /**

-     * Tests the addition and removal of an index because of the change of column order.

-     */

-    public void testChangeIndexColumnOrder()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "      <index-column name='Col2'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col2'/>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

-        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

-

-        assertEquals("TestIndex",

-                     change1.getChangedIndex().getName());

-        assertEquals("TestIndex",

-                     change2.getNewIndex().getName());

-    }

-

-    /**

-     * Tests the addition and removal of an index because of the addition of an index column.

-     */

-    public void testAddIndexColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "      <index-column name='Col2'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

-        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

-

-        assertEquals("TestIndex",

-                     change1.getChangedIndex().getName());

-        assertEquals("TestIndex",

-                     change2.getNewIndex().getName());

-    }

-

-    /**

-     * Tests the addition and removal of an index because of the removal of an index column.

-     */

-    public void testRemoveIndexColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "      <index-column name='Col2'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='INTEGER'/>\n" +

-            "    <column name='Col2' type='DOUBLE'/>\n" +

-            "    <index name='TestIndex'>\n" +

-            "      <index-column name='Col1'/>\n" +

-            "    </index>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(2,

-                     changes.size());

-

-        RemoveIndexChange change1 = (RemoveIndexChange)changes.get(0);

-        AddIndexChange    change2 = (AddIndexChange)changes.get(1);

-

-        assertEquals("TestIndex",

-                     change1.getChangedIndex().getName());

-        assertEquals("TestIndex",

-                     change2.getNewIndex().getName());

-    }

-

-    /**

-     * Tests the addition of a primary key.

-     */

-    public void testAddPrimaryKey()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        AddPrimaryKeyChange change = (AddPrimaryKeyChange)changes.get(0);

-

-        assertEquals(1,

-                     change.getPrimaryKeyColumns().length);

-        assertEquals("ColPK",

-                     change.getPrimaryKeyColumns()[0].getName());

-    }

-

-    /**

-     * Tests the removal of a primary key.

-     */

-    public void testRemovePrimaryKey()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        RemovePrimaryKeyChange change = (RemovePrimaryKeyChange)changes.get(0);

-

-        assertEquals(1,

-                     change.getPrimaryKeyColumns().length);

-        assertEquals("ColPK",

-                     change.getPrimaryKeyColumns()[0].getName());

-    }

-

-    /**

-     * Tests the addition of a column to the primary key.

-     */

-    public void testAddPrimaryKeyColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        PrimaryKeyChange change = (PrimaryKeyChange)changes.get(0);

-

-        assertEquals(1,

-                     change.getOldPrimaryKeyColumns().length);

-        assertEquals(2,

-                     change.getNewPrimaryKeyColumns().length);

-        assertEquals("ColPK1",

-                     change.getOldPrimaryKeyColumns()[0].getName());

-        assertEquals("ColPK1",

-                     change.getNewPrimaryKeyColumns()[0].getName());

-        assertEquals("ColPK2",

-                     change.getNewPrimaryKeyColumns()[1].getName());

-    }

-

-    /**

-     * Tests the removal of a column from the primary key.

-     */

-    public void testRemovePrimaryKeyColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK1' type='INTEGER' required='true'/>\n" +

-            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        PrimaryKeyChange change = (PrimaryKeyChange)changes.get(0);

-

-        assertEquals(2,

-                     change.getOldPrimaryKeyColumns().length);

-        assertEquals(1,

-                     change.getNewPrimaryKeyColumns().length);

-        assertEquals("ColPK1",

-                     change.getOldPrimaryKeyColumns()[0].getName());

-        assertEquals("ColPK2",

-                     change.getOldPrimaryKeyColumns()[1].getName());

-        assertEquals("ColPK2",

-                     change.getNewPrimaryKeyColumns()[0].getName());

-    }

-

-    /**

-     * Tests the addition a column.

-     */

-    public void testAddColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='DOUBLE'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        AddColumnChange change = (AddColumnChange)changes.get(0);

-

-        assertEquals("Col1",

-                     change.getNewColumn().getName());

-    }

-

-    /**

-     * Tests the removal of a column.

-     */

-    public void testChangeColumnOrder()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='DOUBLE'/>\n" +

-            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

-            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

-            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

-            "    <column name='Col1' type='DOUBLE'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnOrderChange change      = (ColumnOrderChange)changes.get(0);

-        Table             sourceTable = change.getChangedTable();

-

-        assertEquals(3,

-                     change.getNewPosition(sourceTable.getColumn(1)));

-        assertEquals(-1,

-                     change.getNewPosition(sourceTable.getColumn(2)));

-        assertEquals(1,

-                     change.getNewPosition(sourceTable.getColumn(3)));

-    }

-

-    /**

-     * Tests the removal of a column.

-     */

-    public void testRemoveColumn()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col1' type='DOUBLE'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        RemoveColumnChange change = (RemoveColumnChange)changes.get(0);

-

-        assertEquals("Col1",

-                     change.getChangedColumn().getName());

-    }

-

-    /**

-     * Tests changing the data type of a column.

-     */

-    public void testChangeColumnDataType()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='DOUBLE'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnDataTypeChange change = (ColumnDataTypeChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-        assertEquals(Types.INTEGER,

-                     change.getNewTypeCode());

-    }

-

-    /**

-     * Tests changing the size of a column.

-     */

-    public void testChangeColumnSize()

-    {

-        // note that we also have a size for the INTEGER column, but we don't

-        // expect a change for it because the size is not relevant for this type

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' size='8' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='VARCHAR' size='16'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='VARCHAR' size='32'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnSizeChange change = (ColumnSizeChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-        assertEquals(32,

-                     change.getNewSize());

-        assertEquals(0,

-                     change.getNewScale());

-    }

-

-    /**

-     * Tests changing the scale of a column.

-     */

-    public void testChangeColumnScale()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='NUMERIC' size='32,0'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='NUMERIC' size='32,5'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnSizeChange change = (ColumnSizeChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-        assertEquals(32,

-                     change.getNewSize());

-        assertEquals(5,

-                     change.getNewScale());

-    }

-

-    /**

-     * Tests removing the size of a column. This test shows how the comparator

-     * reacts in the common case of comparing a model read from a live database

-     * (which usually returns sizes for every column) and a model from XML.

-     * The model comparator will filter out these changes depending on the

-     * platform info with which the comparator was created. 

-     */

-    public void testRemoveColumnSize()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' size='8'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertTrue(changes.isEmpty());

-    }

-

-    /**

-     * Tests changing the default value of a column.

-     */

-    public void testChangeDefaultValue()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' default='1'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' default='2'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnDefaultValueChange change = (ColumnDefaultValueChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-        assertEquals("2",

-                     change.getNewDefaultValue());

-    }

-

-    /**

-     * Tests that shows that the same default value expressed differently does not

-     * result in a change.

-     */

-    public void testSameDefaultValueExpressedDifferently()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='DOUBLE' default='10'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='DOUBLE' default='1e+1'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertTrue(changes.isEmpty());

-    }

-

-    /**

-     * Tests adding a default value to a column.

-     */

-    public void testAddDefaultValue()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' default='0'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(true).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnDefaultValueChange change = (ColumnDefaultValueChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-        assertEquals("0",

-                     change.getNewDefaultValue());

-    }

-

-    /**

-     * Tests chainging the required-constraint of a column.

-     */

-    public void testChangeColumnRequired()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' required='false'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='INTEGER' required='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnRequiredChange change = (ColumnRequiredChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-    }

-

-    /**

-     * Tests chainging the auto-increment-constraint of a column.

-     */

-    public void testChangeColumnAutoIncrement()

-    {

-        final String MODEL1 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TableA'>\n" +

-            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='Col' type='INTEGER' autoIncrement='true'/>\n" +

-            "  </table>\n" +

-            "</database>";

-        final String MODEL2 = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

-            "<database name='test'>\n" +

-            "  <table name='TABLEA'>\n" +

-            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

-            "    <column name='COL' type='INTEGER' autoIncrement='false'/>\n" +

-            "  </table>\n" +

-            "</database>";

-

-        Database model1  = parseDatabaseFromString(MODEL1);

-        Database model2  = parseDatabaseFromString(MODEL2);

-        List     changes = createModelComparator(false).compare(model1, model2);

-

-        assertEquals(1,

-                     changes.size());

-

-        ColumnAutoIncrementChange change = (ColumnAutoIncrementChange)changes.get(0);

-

-        assertEquals("Col",

-                     change.getChangedColumn().getName());

-    }

-}

diff --git a/src/test/org/apache/ddlutils/alteration/TestModelComparison.java b/src/test/org/apache/ddlutils/alteration/TestModelComparison.java
new file mode 100644
index 0000000..ab13744
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestModelComparison.java
@@ -0,0 +1,439 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+import java.util.List;

+

+import org.apache.ddlutils.model.Database;

+

+/**

+ * Tests the comparison on the model level.

+ * 

+ * @version $Revision: $

+ */

+public class TestModelComparison extends TestComparisonBase

+{

+    /**

+     * Tests the addition of a table.

+     */

+    public void testAddTable()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddTableChange change = (AddTableChange)changes.get(0);

+

+        assertTable("TABLEB", null, 1, 0, 0,

+                    change.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     change.getNewTable().getColumn(0));

+    }

+

+    /**

+     * Tests the addition of a table with an index.

+     */

+    public void testAddTableWithIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLA' type='DOUBLE'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='COLA'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddTableChange change = (AddTableChange)changes.get(0);

+

+        assertTable("TABLEB", null, 2, 0, 1, 

+                     change.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     change.getNewTable().getColumn(0));

+        assertColumn("COLA", Types.DOUBLE, null, null, false, false, false,

+                     change.getNewTable().getColumn(1));

+        assertIndex("TestIndex", false, new String[] { "COLA" },

+                     change.getNewTable().getIndex(0));

+    }

+

+    /**

+     * Tests the addition of a table and a foreign key to it.

+     */

+    public void testAddTableAndForeignKeyToIt()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFKB' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        AddTableChange      tableChange = (AddTableChange)changes.get(0);

+        AddForeignKeyChange fkChange    = (AddForeignKeyChange)changes.get(1);

+

+        assertTable("TABLEB", null, 1, 0, 0, 

+                    tableChange.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     tableChange.getNewTable().getColumn(0));

+

+        assertEquals("TABLEA",

+                     fkChange.getChangedTable());

+        assertForeignKey("TESTFKB", "TABLEB", new String[] { "COLFK" }, new String[] { "COLPK" }, 

+                         fkChange.getNewForeignKey());

+    }

+

+    /**

+     * Tests the addition of two tables with foreign keys to each other .

+     */

+    public void testAddTablesWithForeignKeys()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFKB' foreignTable='TABLEB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFKA' foreignTable='TABLEA'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        AddTableChange      tableChange1 = (AddTableChange)changes.get(0);

+        AddTableChange      tableChange2 = (AddTableChange)changes.get(1);

+        AddForeignKeyChange fkChange1    = (AddForeignKeyChange)changes.get(2);

+        AddForeignKeyChange fkChange2    = (AddForeignKeyChange)changes.get(3);

+

+        assertTable("TABLEA", null, 2, 0, 0, 

+                    tableChange1.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     tableChange1.getNewTable().getColumn(0));

+        assertColumn("COLFK", Types.INTEGER, null, null, false, false, false,

+                     tableChange1.getNewTable().getColumn(1));

+

+        assertTable("TABLEB", null, 2, 0, 0, 

+                    tableChange2.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     tableChange2.getNewTable().getColumn(0));

+        assertColumn("COLFK", Types.INTEGER, null, null, false, false, false,

+                     tableChange2.getNewTable().getColumn(1));

+

+        assertEquals("TABLEA",

+                     fkChange1.getChangedTable());

+        assertForeignKey("TESTFKB", "TABLEB", new String[] { "COLFK" }, new String[] { "COLPK" }, 

+                         fkChange1.getNewForeignKey());

+

+        assertEquals("TABLEB",

+                     fkChange2.getChangedTable());

+        assertForeignKey("TESTFKA", "TABLEA", new String[] { "COLFK" }, new String[] { "COLPK" }, 

+                         fkChange2.getNewForeignKey());

+    }

+

+    /**

+     * Tests the removal of a table.

+     */

+    public void testDropTable()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveTableChange change = (RemoveTableChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+    }

+

+    /**

+     * Tests the removal of a table with an index.

+     */

+    public void testDropTableWithIndex()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColA' type='INTEGER'/>\n" +

+            "    <index name='TestIndex'>\n" +

+            "      <index-column name='ColA'/>\n" +

+            "    </index>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveTableChange change = (RemoveTableChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+    }

+

+    /**

+     * Tests the removal of a table with a foreign key.

+     */

+    public void testDropTableWithForeignKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFKB' foreignTable='TableB'>\n" +

+            "      <reference local='COLFK' foreign='COLPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange    = (RemoveForeignKeyChange)changes.get(0);

+        RemoveTableChange      tableChange = (RemoveTableChange)changes.get(1);

+

+        assertEquals("TableA",

+                     fkChange.getChangedTable());

+        assertEquals(model1.findTable("TableA").getForeignKey(0),

+                     fkChange.findChangedForeignKey(model1, false));

+

+        assertEquals("TableA",

+                     tableChange.getChangedTable());

+    }

+

+    /**

+     * Tests the removal of a table and a foreign key to it.

+     */

+    public void testDropTableAndForeignKeyToIt()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "  <table name='TableB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "    <foreign-key name='TESTFKA' foreignTable='TableA'>\n" +

+            "      <reference local='COLFK' foreign='ColPK'/>\n" +

+            "    </foreign-key>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEB'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLFK' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveForeignKeyChange fkChange    = (RemoveForeignKeyChange)changes.get(0);

+        RemoveTableChange      tableChange = (RemoveTableChange)changes.get(1);

+

+        assertEquals("TableB",

+                     fkChange.getChangedTable());

+        assertEquals(model1.findTable("TableB").getForeignKey(0),

+                     fkChange.findChangedForeignKey(model1, false));

+

+        assertEquals("TableA",

+                     tableChange.getChangedTable());

+    }

+

+    /**

+     * Tests the addition and removal of a table.

+     */

+    public void testAddAndDropTable()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveTableChange change1 = (RemoveTableChange)changes.get(0);

+        AddTableChange    change2 = (AddTableChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+

+        assertTable("TABLEA", null, 1, 0, 0, 

+                    change2.getNewTable());

+        assertColumn("COLPK", Types.INTEGER, null, null, true, true, false,

+                     change2.getNewTable().getColumn(0));

+    }

+}

diff --git a/src/test/org/apache/ddlutils/alteration/TestPrimaryKeyComparison.java b/src/test/org/apache/ddlutils/alteration/TestPrimaryKeyComparison.java
new file mode 100644
index 0000000..9691d30
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestPrimaryKeyComparison.java
@@ -0,0 +1,576 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+import java.util.List;

+

+import org.apache.ddlutils.model.Database;

+

+/**

+ * Tests the model comparison of primary keys.

+ * 

+ * @version $Revision: $

+ */

+public class TestPrimaryKeyComparison extends TestComparisonBase

+{

+    /**

+     * Tests the addition of a column that is the primary key.

+     */

+    public void testAddPrimaryKeyColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        AddColumnChange     colChange = (AddColumnChange)changes.get(0);

+        AddPrimaryKeyChange pkChange  = (AddPrimaryKeyChange)changes.get(1);

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertColumn("ColPK1", Types.INTEGER, null, null, false, true, false,

+                     colChange.getNewColumn());

+        assertNull(colChange.getPreviousColumn());

+        assertEquals("ColPK2",

+                     colChange.getNextColumn());

+

+        assertEquals("TableA",

+                     pkChange.getChangedTable());

+        assertEquals(1,

+                     pkChange.getPrimaryKeyColumns().length);

+        assertEquals("ColPK1",

+                     pkChange.getPrimaryKeyColumns()[0]);

+    }

+

+    /**

+     * Tests the addition of a single-column primary key.

+     */

+    public void testMakeColumnPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddPrimaryKeyChange change = (AddPrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals(1,

+                     change.getPrimaryKeyColumns().length);

+        assertEquals("ColPK",

+                     change.getPrimaryKeyColumns()[0]);

+    }

+

+    /**

+     * Tests the addition of a column to the primary key.

+     */

+    public void testAddColumnToPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        PrimaryKeyChange change = (PrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals(2,

+                     change.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK1",

+                     change.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK2",

+                     change.getNewPrimaryKeyColumns()[1]);

+    }

+

+    /**

+     * Tests changing the order of columns in the primary key.

+     */

+    public void testChangeColumnOrderInPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        PrimaryKeyChange  pkChange  = (PrimaryKeyChange)changes.get(0);

+        ColumnOrderChange colChange = (ColumnOrderChange)changes.get(1);

+

+        assertEquals("TableA",

+                     pkChange.getChangedTable());

+        assertEquals(3,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK2",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK3",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        assertEquals("ColPK1",

+                     pkChange.getNewPrimaryKeyColumns()[2]);

+        

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals(2,

+                     colChange.getNewPosition("ColPK1", true));

+        assertEquals(0,

+                     colChange.getNewPosition("ColPK2", true));

+        assertEquals(1,

+                     colChange.getNewPosition("ColPK3", true));

+    }

+

+    /**

+     * Tests adding a column to and changing the order of columns in the primary key.

+     */

+    public void testAddColumnAndChangeColumnOrderInPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(4,

+                     changes.size());

+

+        PrimaryKeyChange  pkChange1  = (PrimaryKeyChange)changes.get(0);

+        ColumnOrderChange colChange1 = (ColumnOrderChange)changes.get(1);

+        AddColumnChange   colChange2 = (AddColumnChange)changes.get(2);

+        PrimaryKeyChange  pkChange2  = (PrimaryKeyChange)changes.get(3);

+        

+        assertEquals("TableA",

+                     pkChange1.getChangedTable());

+        assertEquals(2,

+                     pkChange1.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK3",

+                     pkChange1.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK1",

+                     pkChange1.getNewPrimaryKeyColumns()[1]);

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertEquals(1,

+                     colChange1.getNewPosition("ColPK1", true));

+        assertEquals(-1,

+                     colChange1.getNewPosition("ColPK2", true));

+        assertEquals(0,

+                     colChange1.getNewPosition("ColPK3", true));

+

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertColumn("ColPK2", Types.INTEGER, null, null, false, true, false,

+                     colChange2.getNewColumn());

+        assertNull(colChange2.getPreviousColumn());

+        assertEquals("ColPK3",

+                     colChange2.getNextColumn());

+

+        assertEquals("TableA",

+                     pkChange2.getChangedTable());

+        assertEquals(3,

+                     pkChange2.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK2",

+                     pkChange2.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK3",

+                     pkChange2.getNewPrimaryKeyColumns()[1]);

+        assertEquals("ColPK1",

+                     pkChange2.getNewPrimaryKeyColumns()[2]);

+    }

+

+    /**

+     * Tests removing a column from and changing the order of columns in the primary key.

+     */

+    public void testRemoveColumnAndChangeColumnOrderInPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(3,

+                     changes.size());

+

+        RemoveColumnChange colChange1 = (RemoveColumnChange)changes.get(0);

+        PrimaryKeyChange   pkChange   = (PrimaryKeyChange)changes.get(1);

+        ColumnOrderChange  colChange2 = (ColumnOrderChange)changes.get(2);

+

+        assertEquals("TableA",

+                     colChange1.getChangedTable());

+        assertEquals("ColPK2",

+                     colChange1.getChangedColumn());

+

+        assertEquals("TableA",

+                     pkChange.getChangedTable());

+        assertEquals(2,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK3",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK1",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+        

+        assertEquals("TableA",

+                     colChange2.getChangedTable());

+        assertEquals(1,

+                     colChange2.getNewPosition("ColPK1", true));

+        assertEquals(-1,

+                     colChange2.getNewPosition("ColPK2", true));

+        assertEquals(0,

+                     colChange2.getNewPosition("ColPK3", true));

+    }

+

+    // TODO: remove, add & reorder PK columns

+    /**

+     * Tests the removal of a column from the primary key.

+     */

+    public void testMakeColumnNotPrimaryKey()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        PrimaryKeyChange change = (PrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals(1,

+                     change.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK2",

+                     change.getNewPrimaryKeyColumns()[0]);

+    }

+

+

+    /**

+     * Tests removing the column that is the primary key.

+     */

+    public void testDropPrimaryKeyColumn1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COL' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveColumnChange colChange = (RemoveColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals("ColPK",

+                     colChange.getChangedColumn());

+    }

+

+    /**

+     * Tests dropping a column that is part of the primary key.

+     */

+    public void testDropPrimaryKeyColumn2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveColumnChange colChange = (RemoveColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     colChange.getChangedTable());

+        assertEquals("ColPK2",

+                     colChange.getChangedColumn());

+    }

+

+    /**

+     * Tests the removal of a primary key.

+     */

+    public void testRemovePrimaryKey1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemovePrimaryKeyChange change = (RemovePrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+    }

+

+    /**

+     * Tests removing a multi-column primary key.

+     */

+    public void testRemovePrimaryKey2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK1' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='COLPK3' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemovePrimaryKeyChange pkChange = (RemovePrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     pkChange.getChangedTable());

+    }

+

+    /**

+     * Tests changing the columns of a primary key.

+     */

+    public void testChangePrimaryKeyColumns()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK4' type='INTEGER' required='true'/>\n" +

+            "    <column name='ColPK5' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='ColPK1' type='INTEGER' required='true'/>\n" +

+            "    <column name='ColPK2' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK3' type='INTEGER' required='true'/>\n" +

+            "    <column name='ColPK4' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColPK5' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        PrimaryKeyChange pkChange = (PrimaryKeyChange)changes.get(0);

+

+        assertEquals("TableA",

+                     pkChange.getChangedTable());

+        assertEquals(2,

+                     pkChange.getNewPrimaryKeyColumns().length);

+        assertEquals("ColPK2",

+                     pkChange.getNewPrimaryKeyColumns()[0]);

+        assertEquals("ColPK4",

+                     pkChange.getNewPrimaryKeyColumns()[1]);

+    }

+}

diff --git a/src/test/org/apache/ddlutils/alteration/TestTableComparison.java b/src/test/org/apache/ddlutils/alteration/TestTableComparison.java
new file mode 100644
index 0000000..f46f10a
--- /dev/null
+++ b/src/test/org/apache/ddlutils/alteration/TestTableComparison.java
@@ -0,0 +1,1249 @@
+package org.apache.ddlutils.alteration;

+

+/*

+ * Licensed to the Apache Software Foundation (ASF) under one

+ * or more contributor license agreements.  See the NOTICE file

+ * distributed with this work for additional information

+ * regarding copyright ownership.  The ASF licenses this file

+ * to you under the Apache License, Version 2.0 (the

+ * "License"); you may not use this file except in compliance

+ * with the License.  You may obtain a copy of the License at

+ *

+ *   http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing,

+ * software distributed under the License is distributed on an

+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY

+ * KIND, either express or implied.  See the License for the

+ * specific language governing permissions and limitations

+ * under the License.

+ */

+

+import java.sql.Types;

+import java.util.List;

+

+import org.apache.ddlutils.model.Database;

+

+/**

+ * Tests the model comparison of tables.

+ * 

+ * @version $Revision: $

+ */

+public class TestTableComparison extends TestComparisonBase

+{

+    /**

+     * Tests the addition a column.

+     */

+    public void testAddColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddColumnChange change = (AddColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col1", Types.DOUBLE, null, null, false, false, false,

+                     change.getNewColumn());

+        assertEquals("ColPK",

+                     change.getPreviousColumn());

+        assertNull(change.getNextColumn());

+    }

+

+    /**

+     * Tests the addition of an auto-increment column.

+     */

+    public void testAddAutoIncrementColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColA' type='INTEGER' autoIncrement='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddColumnChange change = (AddColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("ColA", Types.INTEGER, null, null, false, false, true,

+                     change.getNewColumn());

+        assertEquals("ColPK",

+                     change.getPreviousColumn());

+        assertNull(change.getNextColumn());

+    }

+

+    /**

+     * Tests the addition of a required column.

+     */

+    public void testAddRequiredColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='ColA' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddColumnChange change = (AddColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("ColA", Types.INTEGER, null, null, false, true, false,

+                     change.getNewColumn());

+        assertEquals("ColPK",

+                     change.getPreviousColumn());

+        assertNull(change.getNextColumn());

+    }

+

+    /**

+     * Tests the addition of a column that has a size spec and a default value.

+     */

+    public void testAddColumnWithSizeAndDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COLA' type='VARCHAR' size='32' default='text'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        AddColumnChange change = (AddColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("COLA", Types.VARCHAR, "32", "text", false, false, false,

+                     change.getNewColumn());

+        assertEquals("ColPK",

+                     change.getPreviousColumn());

+        assertNull(change.getNextColumn());

+    }

+

+    /**

+     * Tests making a column required.

+     */

+    public void testMakeColumnRequired()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' required='false'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, null, false, true, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests making a column not required.

+     */

+    public void testMakeColumnNotRequired()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER' required='false'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, null, false, false, false,

+                     change.getNewColumn());

+    }

+    

+    /**

+     * Tests making a column auto-increment.

+     */

+    public void testMakeColumnAutoIncrement()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' autoIncrement='false'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER' autoIncrement='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, null, false, false, true,

+                     change.getNewColumn());

+    }

+    

+    /**

+     * Tests making a column not auto-increment.

+     */

+    public void testMakeColumnNotAutoIncrement()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' autoIncrement='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER' autoIncrement='false'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType1()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType2()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.VARCHAR, "32", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType3()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='NUMERIC' size='10,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.NUMERIC, "10,5", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType4()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='CHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.CHAR, "32", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType5()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='NUMERIC' size='32,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.NUMERIC, "32,5", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType6()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DECIMAL' size='10,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.VARCHAR, "32", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType7()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='FLOAT'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.FLOAT, null, null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the data type of a column.

+     */

+    public void testChangeColumnDataType8()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DECIMAL' size='10,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='TIMESTAMP'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.TIMESTAMP, null, null, false, false, false,

+                     change.getNewColumn());

+    }

+    

+    /**

+     * Tests changing the size of a column.

+     */

+    public void testChangeColumnSize()

+    {

+        // note that we also have a size for the INTEGER column, but we don't

+        // expect a change for it because the size is not relevant for this type

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' size='8' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='16'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.VARCHAR, "32", null, false, false, false,

+                     change.getNewColumn());

+    }

+    

+    /**

+     * Tests changing the precision & scale of a column.

+     */

+    public void testChangeColumnPrecisionAndScale()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DECIMAL' size='16,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='DECIMAL' size='32,7'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.DECIMAL, "32,7", null, false, false, false,

+                     change.getNewColumn());

+    }

+    

+    /**

+     * Tests changing the scale of a column.

+     */

+    public void testChangeColumnScale()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='NUMERIC' size='32,0'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='NUMERIC' size='32,5'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.NUMERIC, "32,5", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests removing the size of a column. This test shows how the comparator

+     * reacts in the common case of comparing a model read from a live database

+     * (which usually returns sizes for every column) and a model from XML.

+     * The model comparator will filter out these changes depending on the

+     * platform info with which the comparator was created. 

+     */

+    public void testRemoveUnnecessaryColumnSize()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' size='8'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertTrue(changes.isEmpty());

+    }

+

+    /**

+     * Tests adding a default value to a column.

+     */

+    public void testAddDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' default='0'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, "0", false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the default value of a column.

+     */

+    public void testChangeDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' default='1'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' default='2'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, "2", false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests that shows that the same default value expressed differently does not

+     * result in a change.

+     */

+    public void testSameDefaultValueExpressedDifferently()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='DOUBLE' default='10'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TABLEA'>\n" +

+            "    <column name='COLPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='COL' type='DOUBLE' default='1e+1'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertTrue(changes.isEmpty());

+    }

+

+    /**

+     * Tests removing the default value of a column.

+     */

+    public void testRemoveDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='16' default='1'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='16'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.VARCHAR, "16", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests making a columnb required with a new default value.

+     */

+    public void testMakeColumnRequiredWithDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='INTEGER' required='true' default='0'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.INTEGER, null, "0", false, true, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests making a column not required and removing the default value.

+     */

+    public void testMakeColumnNotRequiredWithoutDefaultValue()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='16' default='1' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col' type='VARCHAR' size='16'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnDefinitionChange change = (ColumnDefinitionChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertColumn("Col", Types.VARCHAR, "16", null, false, false, false,

+                     change.getNewColumn());

+    }

+

+    /**

+     * Tests changing the order of the columns in a table.

+     */

+    public void testChangeColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        ColumnOrderChange change = (ColumnOrderChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals(3,

+                     change.getNewPosition("Col1", true));

+        assertEquals(-1,

+                     change.getNewPosition("Col2", true));

+        assertEquals(1,

+                     change.getNewPosition("Col3", true));

+    }

+

+    /**

+     * Tests adding a column and changing the order of the existing columns in a table.

+     */

+    public void testAddColumnAndChangeColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        ColumnOrderChange change1 = (ColumnOrderChange)changes.get(0);

+        AddColumnChange   change2 = (AddColumnChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals(2,

+                     change1.getNewPosition("Col1", false));

+        assertEquals(1,

+                     change1.getNewPosition("Col2", false));

+        

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertColumn("Col3", Types.VARCHAR, "32", null, false, false, false,

+                     change2.getNewColumn());

+        assertEquals("ColPK",

+                     change2.getPreviousColumn());

+        assertEquals("Col2",

+                     change2.getNextColumn());

+    }

+

+    /**

+     * Tests removing a column and changing the order of the existing columns in a table.

+     */

+    public void testRemoveColumnAndChangeColumnOrder()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col3' type='VARCHAR' size='32'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col2' type='INTEGER' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(true).getChanges(model1, model2);

+

+        assertEquals(2,

+                     changes.size());

+

+        RemoveColumnChange change1 = (RemoveColumnChange)changes.get(0);

+        ColumnOrderChange  change2 = (ColumnOrderChange)changes.get(1);

+

+        assertEquals("TableA",

+                     change1.getChangedTable());

+        assertEquals("Col3",

+                     change1.getChangedColumn());

+

+        assertEquals("TableA",

+                     change2.getChangedTable());

+        assertEquals(-1,

+                     change2.getNewPosition("ColPK", true));

+        assertEquals(2,

+                     change2.getNewPosition("Col1", true));

+        assertEquals(1,

+                     change2.getNewPosition("Col2", true));

+    }

+    

+    /**

+     * Tests the removal of a column.

+     */

+    public void testDropColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='DOUBLE'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveColumnChange change = (RemoveColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals("Col1",

+                     change.getChangedColumn());

+    }

+    

+    /**

+     * Tests the removal of an auto-increment column.

+     */

+    public void testDropAutoIncrementColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER' autoIncrement='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveColumnChange change = (RemoveColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals("Col1",

+                     change.getChangedColumn());

+    }

+    

+    /**

+     * Tests the removal of a required column.

+     */

+    public void testDropRequiredColumn()

+    {

+        final String MODEL1 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "    <column name='Col1' type='INTEGER' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+        final String MODEL2 = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n" +

+            "<database name='test'>\n" +

+            "  <table name='TableA'>\n" +

+            "    <column name='ColPK' type='INTEGER' primaryKey='true' required='true'/>\n" +

+            "  </table>\n" +

+            "</database>";

+

+        Database model1  = parseDatabaseFromString(MODEL1);

+        Database model2  = parseDatabaseFromString(MODEL2);

+        List     changes = getPlatform(false).getChanges(model1, model2);

+

+        assertEquals(1,

+                     changes.size());

+

+        RemoveColumnChange change = (RemoveColumnChange)changes.get(0);

+

+        assertEquals("TableA",

+                     change.getChangedTable());

+        assertEquals("Col1",

+                     change.getChangedColumn());

+    }

+}

diff --git a/src/test/org/apache/ddlutils/io/RoundtripTestBase.java b/src/test/org/apache/ddlutils/io/RoundtripTestBase.java
index 7798116..9ec2802 100644
--- a/src/test/org/apache/ddlutils/io/RoundtripTestBase.java
+++ b/src/test/org/apache/ddlutils/io/RoundtripTestBase.java
@@ -36,6 +36,7 @@
 import org.apache.ddlutils.dynabean.SqlDynaClass;

 import org.apache.ddlutils.dynabean.SqlDynaProperty;

 import org.apache.ddlutils.model.CascadeActionEnum;

+import org.apache.ddlutils.model.CloneHelper;

 import org.apache.ddlutils.model.Column;

 import org.apache.ddlutils.model.Database;

 import org.apache.ddlutils.model.ForeignKey;

@@ -268,88 +269,89 @@
      */

     protected Database getAdjustedModel()

     {

-        try

+        Database model = new CloneHelper().clone(getModel());

+

+        for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++)

         {

-            Database model = (Database)getModel().clone();

+            Table table = model.getTable(tableIdx);

 

-            for (int tableIdx = 0; tableIdx < model.getTableCount(); tableIdx++)

+            for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++)

             {

-                Table table = model.getTable(tableIdx);

+                Column column     = table.getColumn(columnIdx);

+                int    origType   = column.getTypeCode();

+                int    targetType = getPlatformInfo().getTargetJdbcType(origType);

 

-                for (int columnIdx = 0; columnIdx < table.getColumnCount(); columnIdx++)

+                // we adjust the column types if the native type would back-map to a

+                // different jdbc type

+                if (targetType != origType)

                 {

-                    Column column     = table.getColumn(columnIdx);

-                    int    origType   = column.getTypeCode();

-                    int    targetType = getPlatformInfo().getTargetJdbcType(origType);

-

-                    // we adjust the column types if the native type would back-map to a

-                    // different jdbc type

-                    if (targetType != origType)

+                    column.setTypeCode(targetType);

+                    // we should also adapt the default value

+                    if (column.getDefaultValue() != null)

                     {

-                        column.setTypeCode(targetType);

-                        // we should also adapt the default value

-                        if (column.getDefaultValue() != null)

-                        {

-                            DefaultValueHelper helper = getPlatform().getSqlBuilder().getDefaultValueHelper();

+                        DefaultValueHelper helper = getPlatform().getSqlBuilder().getDefaultValueHelper();

 

-                            column.setDefaultValue(helper.convert(column.getDefaultValue(), origType, targetType));

-                        }

-                    }

-                    // we also promote the default size if the column has no size

-                    // spec of its own

-                    if ((column.getSize() == null) && getPlatformInfo().hasSize(targetType))

-                    {

-                        Integer defaultSize = getPlatformInfo().getDefaultSize(targetType);

-

-                        if (defaultSize != null)

-                        {

-                            column.setSize(defaultSize.toString());

-                        }

-                    }

-                    // finally the platform might return a synthetic default value if the column

-                    // is a primary key column

-                    if (getPlatformInfo().isSyntheticDefaultValueForRequiredReturned() &&

-                        (column.getDefaultValue() == null) && column.isRequired() && !column.isAutoIncrement())

-                    {

-                        switch (column.getTypeCode())

-                        {

-                            case Types.TINYINT:

-                            case Types.SMALLINT:

-                            case Types.INTEGER:

-                            case Types.BIGINT:

-                                column.setDefaultValue("0");

-                                break;

-                            case Types.REAL:

-                            case Types.FLOAT:

-                            case Types.DOUBLE:

-                                column.setDefaultValue("0.0");

-                                break;

-                            case Types.BIT:

-                                column.setDefaultValue("false");

-                                break;

-                            default:

-                                column.setDefaultValue("");

-                                break;

-                        }

+                        column.setDefaultValue(helper.convert(column.getDefaultValue(), origType, targetType));

                     }

                 }

-                // we also add the default names to foreign keys that are initially unnamed

-                for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++)

+                // we also promote the default size if the column has no size

+                // spec of its own

+                if ((column.getSize() == null) && getPlatformInfo().hasSize(targetType))

                 {

-                    ForeignKey fk = table.getForeignKey(fkIdx);

+                    Integer defaultSize = getPlatformInfo().getDefaultSize(targetType);

 

-                    if (fk.getName() == null)

+                    if (defaultSize != null)

                     {

-                        fk.setName(getPlatform().getSqlBuilder().getForeignKeyName(table, fk));

+                        column.setSize(defaultSize.toString());

                     }

                 }

+                // finally the platform might return a synthetic default value if the column

+                // is a primary key column

+                if (getPlatformInfo().isSyntheticDefaultValueForRequiredReturned() &&

+                    (column.getDefaultValue() == null) && column.isRequired() && !column.isAutoIncrement())

+                {

+                    switch (column.getTypeCode())

+                    {

+                        case Types.TINYINT:

+                        case Types.SMALLINT:

+                        case Types.INTEGER:

+                        case Types.BIGINT:

+                            column.setDefaultValue("0");

+                            break;

+                        case Types.REAL:

+                        case Types.FLOAT:

+                        case Types.DOUBLE:

+                            column.setDefaultValue("0.0");

+                            break;

+                        case Types.BIT:

+                            column.setDefaultValue("false");

+                            break;

+                        default:

+                            column.setDefaultValue("");

+                            break;

+                    }

+                }

+                if (column.isPrimaryKey() && getPlatformInfo().isPrimaryKeyColumnAutomaticallyRequired())

+                {

+                    column.setRequired(true);

+                }

+                if (column.isAutoIncrement() && getPlatformInfo().isIdentityColumnAutomaticallyRequired())

+                {

+                    column.setRequired(true);

+                }

             }

-            return model;

+            // we also add the default names to foreign keys that are initially unnamed

+            for (int fkIdx = 0; fkIdx < table.getForeignKeyCount(); fkIdx++)

+            {

+                ForeignKey fk = table.getForeignKey(fkIdx);

+

+                if (fk.getName() == null)

+                {

+                    fk.setName(getPlatform().getSqlBuilder().getForeignKeyName(table, fk));

+                }

+            }

         }

-        catch (CloneNotSupportedException ex)

-        {

-            throw new RuntimeException(ex);

-        }

+        return model;

     }

 

     /**

diff --git a/src/test/org/apache/ddlutils/io/TestAddColumn.java b/src/test/org/apache/ddlutils/io/TestAddColumn.java
new file mode 100644
index 0000000..354159c
--- /dev/null
+++ b/src/test/org/apache/ddlutils/io/TestAddColumn.java
@@ -0,0 +1,3416 @@
+package org.apache.ddlutils.io;

+

+/*

+ * 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.math.BigDecimal;

+import java.util.List;

+

+import junit.framework.Test;

+

+import org.apache.commons.beanutils.DynaBean;

+import org.apache.ddlutils.platform.db2.Db2Platform;

+import org.apache.ddlutils.platform.db2.Db2v8Platform;

+import org.apache.ddlutils.platform.hsqldb.HsqlDbPlatform;

+import org.apache.ddlutils.platform.interbase.InterbasePlatform;

+import org.apache.ddlutils.platform.maxdb.MaxDbPlatform;

+import org.apache.ddlutils.platform.mysql.MySql50Platform;

+import org.apache.ddlutils.platform.mysql.MySqlPlatform;

+import org.apache.ddlutils.platform.sapdb.SapDbPlatform;

+import org.apache.ddlutils.platform.sybase.SybasePlatform;

+

+/**

+ * Tests database alterations that add columns.

+ * 

+ * @version $Revision: $

+ */

+public class TestAddColumn extends RoundtripTestBase

+{

+    /**

+     * Parameterized test case pattern.

+     * 

+     * @return The tests

+     */

+    public static Test suite() throws Exception

+    {

+        return getTests(TestAddColumn.class);

+    }

+

+    /**

+     * Tests the addition of a column.

+     */

+    public void testAddColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+    	alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of an auto-increment column.

+     */

+    public void testAddAutoIncrementColumn()

+    {

+    	if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+    	{

+    		return;

+    	}

+

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='NUMERIC' size='12,0' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+    	alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

+        }

+        else

+        {

+            Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+            assertTrue((avalue == null) || new Integer(1).equals(avalue));

+        }

+    }

+

+    /**

+     * Tests the addition of another auto-increment column.

+     */

+    public void testAddSecondAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported() ||

+            SybasePlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MaxDbPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            SapDbPlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // Sybase and MaxDb/SapDb does not support more than one identity column per table

+            return;

+        }

+

+        final String model1Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                                 "<database name='roundtriptest'>\n"+

+                                 "  <table name='roundtrip'>\n"+

+                                 "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                                 "    <column name='avalue1' type='INTEGER' autoIncrement='true'/>\n"+

+                                 "  </table>\n"+

+                                 "</database>";

+        final String model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                                 "<database name='roundtriptest'>\n"+

+                                 "  <table name='roundtrip'>\n"+

+                                 "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                                 "    <column name='avalue1' type='INTEGER' autoIncrement='true'/>\n"+

+                                 "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+                                 "  </table>\n"+

+                                 "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans   = getRows("roundtrip");

+        Object avalue1 = ((DynaBean)beans.get(0)).get("avalue1");

+        Object avalue2 = ((DynaBean)beans.get(0)).get("avalue2");

+

+        assertTrue((avalue1 == null) || new Integer(1).equals(avalue1));

+        assertTrue((avalue2 == null) || new Integer(1).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of a column that is set to NOT NULL.

+     */

+    public void testAddRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' default='2' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new BigDecimal(2), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a column with a default value. Note that depending

+     * on whether the database supports this via a statement, this test may fail.

+     * For instance, Sql Server has a statement for this which means that the

+     * existing value in column avalue won't be changed and thus the test fails.

+     */

+    public void testAddColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        // we cannot be sure whether the default algorithm is used (which will apply the

+        // default value even to existing columns with NULL in it) or the database supports

+        // it directly (in which case it might still be NULL)

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Double(2).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of a required auto-increment column.

+     */

+    public void testAddRequiredAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='NUMERIC' size='12,0' autoIncrement='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

+        }

+        else

+        {

+            Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+            assertTrue((avalue == null) || new Integer(1).equals(avalue));

+        }

+    }

+

+    /**

+     * Tests the addition of a column with a default value. Note that depending

+     * on whether the database supports this via a statement, this test may fail.

+     * For instance, Sql Server has a statement for this which means that the

+     * existing value in column avalue won't be changed and thus the test fails.

+     */

+    public void testAddRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='CHAR' size='8' default='sometext' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        // we cannot be sure whether the default algorithm is used (which will apply the

+        // default value even to existing columns with NULL in it) or the database supports

+        // it directly (in which case it might still be NULL)

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || "sometext".equals(avalue));

+    }

+

+    /**

+     * Tests the addition of several columns at the end of the table.

+     */

+    public void testAddMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' default='1.0'/>\n"+

+            "    <column name='avalue4' type='VARCHAR' size='16'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), "test", new Integer(3) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)"test", beans.get(0), "avalue1");

+        assertEquals(new Integer(3), beans.get(0), "avalue2");

+

+        // we cannot be sure whether the default algorithm is used (which will apply the

+        // default value even to existing columns with NULL in it) or the database supports

+        // it dircetly (in which case it might still be NULL)

+        Object avalue3 = ((DynaBean)beans.get(0)).get("avalue3");

+

+        assertTrue((avalue3 == null) || new Double(1.0).equals(avalue3));

+        

+        assertEquals((Object)null, beans.get(0), "avalue4");

+    }

+

+    /**

+     * Tests the addition of a primary key and a column.

+     */

+    public void testAddPKAndColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            // MySql uses an empty string to initialize the new pk column

+            assertEquals((Object)"",     beans.get(0), "pk");

+            assertEquals(new Integer(1), beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a primary key and an autoincrement column.

+     */

+    public void testAddPKAndAutoIncrementColumn()

+    {

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "pk");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+        }

+    }

+

+    /**

+     * Tests the addition of a primary key and multiple columns.

+     */

+    public void testAddPKAndMultipleColumns()

+    {

+        if (Db2Platform.DATABASENAME.equals(getPlatform().getName()) ||

+            Db2v8Platform.DATABASENAME.equals(getPlatform().getName()) ||

+            SybasePlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            InterbasePlatform.DATABASENAME.equals(getPlatform().getName())) {

+            // Db2, Sybase, Interbase require that all primary key columns be required, but they don't make them so automatically

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' default='2'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(0),  beans.get(0), "pk1");

+            assertEquals((Object)"",      beans.get(0), "pk2");

+            assertEquals(new Double(2.0), beans.get(0), "pk3");

+            assertEquals(new Integer(1),  beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a primary key and multiple required columns.

+     */

+    public void testAddPKAndMultipleRequiredColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true' default='2'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(0),  beans.get(0), "pk1");

+            assertEquals((Object)"",      beans.get(0), "pk2");

+            assertEquals(new Double(2.0), beans.get(0), "pk3");

+            assertEquals(new Integer(1),  beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a primary key and multiple columns.

+     */

+    public void testAddPKAndMultipleColumnsInclAutoIncrement()

+    {

+        if (HsqlDbPlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // the IDENTITY column has to be the only PK column

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the addition of a column to a primary key.

+     */

+    public void testAddColumnIntoPK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),  beans.get(0), "pk1");

+            assertEquals(new Integer(0),  beans.get(0), "pk2");

+            assertEquals(new Integer(2),  beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of an autoincrement column into the primary key.

+     */

+    public void testAddAutoIncrementColumnIntoPK()

+    {

+        if (HsqlDbPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // the IDENTITY column has to be the only PK column

+            return;

+        }

+

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "    <column name='pk2' type='NUMERIC' size='12,0' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(-1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(-1), ((DynaBean)beans.get(0)).get("pk1"));

+        assertEquals(new Integer(2), ((DynaBean)beans.get(0)).get("avalue"));

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "pk2");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk2");

+        }

+    }

+

+    /**

+     * Tests the addition of multiple columns into the primary key.

+     */

+    public void testAddMultipleColumnsIntoPK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),  beans.get(0), "pk1");

+            assertEquals((Object)"",      beans.get(0), "pk2");

+            assertEquals(new Double(0.0), beans.get(0), "pk3");

+            assertEquals((Object)null,    beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of multiple columns into the primary key which has an auto increment column.

+     */

+    public void testAddMultipleColumnsIntoPKWithAutoIncrement()

+    {

+        if (HsqlDbPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // the IDENTITY column has to be the only PK column

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the addition of multiple columns including one with auto increment into the primary key.

+     */

+    public void testAddMultipleColumnsInclAutoIncrementIntoPK()

+    {

+        if (HsqlDbPlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // the IDENTITY column has to be the only PK column

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), "text" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the addition of a non-unique index and a column.

+     */

+    public void testAddNonUniqueIndexAndColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a non-unique index and an auto increment column.

+     */

+    public void testAddNonUniqueIndexAndAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans  = getRows("roundtrip");

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Integer(1).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of a non-unique index and a required column.

+     */

+    public void testAddNonUniqueIndexAndRequiredColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),    beans.get(0), "pk");

+            assertEquals(new BigDecimal(0), beans.get(0), "avalue");

+        }

+        else if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals((Object)null,   beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a non-unique index and a column with a default value.

+     */

+    public void testAddNonUniqueIndexAndColumnWithDefault()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans  = getRows("roundtrip");

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Double(2).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of a non-unique index and a required auto increment column.

+     */

+    public void testAddNonUniqueIndexAndRequiredAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals((Object)null,   beans.get(0), "avalue");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(1), beans.get(0), "avalue");

+        }

+    }

+

+    /**

+     * Tests the addition of a non-unique index and a required column with a default value.

+     */

+    public void testAddNonUniqueIndexAndRequiredColumnWithDefault()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='sometext'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)"sometext", beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a non-unique index and several columns.

+     */

+    public void testAddNonUniqueIndexAndMultipleColumns()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(1), beans.get(0), "avalue1");

+            assertEquals((Object)"",     beans.get(0), "avalue2");

+        }

+        else if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals((Object)null,   beans.get(0), "avalue1");

+            assertEquals((Object)null,   beans.get(0), "avalue2");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+

+    /**

+     * Tests the addition of an unique index and a column.

+     */

+    public void testAddUniqueIndexAndColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported() ||

+            InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of an unique index and an auto increment column.

+     */

+    public void testAddUniqueIndexAndAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans  = getRows("roundtrip");

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Integer(1).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of an unique index and a required column.

+     */

+    public void testAddUniqueIndexAndRequiredColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),    beans.get(0), "pk");

+            assertEquals(new BigDecimal(0), beans.get(0), "avalue");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of an unique index and a column with a default value.

+     */

+    public void testAddUniqueIndexAndColumnWithDefault()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans  = getRows("roundtrip");

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Double(2).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of an unique index and a required auto increment column.

+     */

+    public void testAddUniqueIndexAndRequiredAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of an unique index and a required column with a default value.

+     */

+    public void testAddUniqueIndexAndRequiredColumnWithDefault()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='sometext'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)"sometext", beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of an unique index and several columns.

+     */

+    public void testAddUniqueIndexAndMultipleColumns()

+    {

+        if (!getPlatformInfo().isIndicesSupported() ||

+            InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(1), beans.get(0), "avalue1");

+            assertEquals((Object)"",     beans.get(0), "avalue2");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of an unique index and several required columns.

+     */

+    public void testAddUniqueIndexAndMultipleRequiredColumns()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true' default='1'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(1), beans.get(0), "avalue1");

+            assertEquals((Object)"",     beans.get(0), "avalue2");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a column into a non-unique index.

+     */

+    public void testAddColumnIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals((Object)null,   beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of an auto increment column into a non-unique index.

+     */

+    public void testAddAutoIncrementColumnIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans   = getRows("roundtrip");

+        Object avalue2 = ((DynaBean)beans.get(0)).get("avalue2");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Integer(1).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of a required column into a non-unique index.

+     */

+    public void testAddRequiredColumnIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),    beans.get(0), "pk");

+            assertEquals(new Integer(2),    beans.get(0), "avalue1");

+            assertEquals(new BigDecimal(0), beans.get(0), "avalue2");

+        }

+        else if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(2), beans.get(0), "avalue1");

+            assertEquals((Object)null,   beans.get(0), "avalue2");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a column with a default value into a non-unique index.

+     */

+    public void testAddColumnWithDefaultIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans   = getRows("roundtrip");

+        Object avalue2 = ((DynaBean)beans.get(0)).get("avalue2");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Double(2).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of a required auto increment column into a non-unique index.

+     */

+    public void testAddRequiredAutoIncrementColumnIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(2), beans.get(0), "avalue1");

+            assertEquals((Object)null,   beans.get(0), "avalue2");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(2), beans.get(0), "avalue1");

+            assertEquals(new Integer(1), beans.get(0), "avalue2");

+        }

+    }

+

+    /**

+     * Tests the addition of a required column with a default value into a non-unique index.

+     */

+    public void testAddRequiredColumnWithDefaultIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' default='sometext' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2),     beans.get(0), "avalue1");

+        assertEquals((Object)"sometext", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of multiple columns into a non-unique index.

+     */

+    public void testAddMultipleColumnsIntoNonUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='3'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue3'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),  beans.get(0), "pk");

+            assertEquals(new Integer(2),  beans.get(0), "avalue1");

+            assertEquals(new Integer(3),  beans.get(0), "avalue2");

+            assertEquals(new Double(0.0), beans.get(0), "avalue3");

+        }

+        else if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(1), beans.get(0), "pk");

+            assertEquals(new Integer(2), beans.get(0), "avalue1");

+            assertEquals((Object)null,   beans.get(0), "avalue2");

+            assertEquals((Object)null,   beans.get(0), "avalue3");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a column into an unique index.

+     */

+    public void testAddColumnIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() ||

+            InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals((Object)null,   beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of an auto increment column into an unique index.

+     */

+    public void testAddAutoIncrementColumnIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() ||

+            !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans   = getRows("roundtrip");

+        Object avalue2 = ((DynaBean)beans.get(0)).get("avalue2");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Integer(1).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of a required column into an unique index.

+     */

+    public void testAddRequiredColumnIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            // MySql uses the default value for the column's type to initialize

+            // the new column for existing rows

+            assertEquals(new Integer(1),    beans.get(0), "pk");

+            assertEquals(new Integer(2),    beans.get(0), "avalue1");

+            assertEquals(new BigDecimal(0), beans.get(0), "avalue2");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a column with a default value into an unique index.

+     */

+    public void testAddColumnWithDefaultIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans   = getRows("roundtrip");

+        Object avalue2 = ((DynaBean)beans.get(0)).get("avalue2");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Double(2).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of a required auto increment column into an unique index.

+     */

+    public void testAddRequiredAutoIncrementColumnIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() || !getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Integer(1), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of a required column with a default value into an unique index.

+     */

+    public void testAddRequiredColumnWithDefaultIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' default='sometext' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2),     beans.get(0), "avalue1");

+        assertEquals((Object)"sometext", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of multiple columns into an unique index.

+     */

+    public void testAddMultipleColumnsIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported() ||

+            InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='3'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "      <unique-column name='avalue3'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),  beans.get(0), "pk");

+            assertEquals(new Integer(2),  beans.get(0), "avalue1");

+            assertEquals(new Integer(3),  beans.get(0), "avalue2");

+            assertEquals(new Double(0.0), beans.get(0), "avalue3");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of multiple required columns into an unique index.

+     */

+    public void testAddMultipleRequiredColumnsIntoUniqueIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true' default='3'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "      <unique-column name='avalue3'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName())) {

+            assertEquals(new Integer(1),  beans.get(0), "pk");

+            assertEquals(new Integer(2),  beans.get(0), "avalue1");

+            assertEquals(new Integer(3),  beans.get(0), "avalue2");

+            assertEquals(new Double(0.0), beans.get(0), "avalue3");

+        }

+        else {

+            assertTrue(beans.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local column.

+     */

+    public void testAddFKAndLocalColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text" });

+        insertRow("roundtrip2", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"text", beans1.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals((Object)null,   beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local auto increment column.

+     */

+    public void testAddFKAndLocalAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans1 = getRows("roundtrip1");

+        List   beans2 = getRows("roundtrip2");

+        Object avalue = ((DynaBean)beans2.get(0)).get("avalue");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertTrue((avalue == null) || new Integer(1).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local required column.

+     */

+    public void testAddFKAndLocalRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new BigDecimal(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new BigDecimal(1), beans1.get(0), "pk");

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(2), beans2.get(0), "pk");

+            assertEquals((Object)null,   beans2.get(0), "avalue");

+        }

+        else

+        {

+            assertTrue(beans2.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local column with a default value.

+     */

+    public void testAddFKAndLocalColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE' default='1'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Double(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans1 = getRows("roundtrip1");

+        List   beans2 = getRows("roundtrip2");

+        Object avalue = ((DynaBean)beans2.get(0)).get("avalue");

+

+        assertEquals(new Double(1),  beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertTrue((avalue == null) || new Double(1).equals(avalue));

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local required auto increment column.

+     */

+    public void testAddFKAndLocalRequiredAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals((Object)null, beans2.get(0), "avalue");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans2.get(0), "avalue");

+        }

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local required column with a default value.

+     */

+    public void testAddFKAndLocalRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='CHAR' size='8' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='CHAR' size='8' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='moretext'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "moretext" });

+        insertRow("roundtrip2", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"moretext", beans1.get(0), "pk");

+        assertEquals(new Integer(1),    beans2.get(0), "pk");

+        assertEquals((Object)"moretext", beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a foreign key and its local columns.

+     */

+    public void testAddFKAndMultipleLocalColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(3) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2),  beans1.get(0), "pk2");

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals(new Integer(3), beans2.get(0), "pk");

+            assertEquals((Object)null,   beans2.get(0), "avalue1");

+            assertEquals((Object)null,   beans2.get(0), "avalue2");

+        }

+        else

+        {

+            assertTrue(beans2.isEmpty());

+        }

+    }

+

+    /**

+     * Tests the addition of a foreign key and its foreign column.

+     */

+    public void testAddFKAndForeignColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the addition of a foreign key and its foreign auto increment column.

+     */

+    public void testAddFKAndForeignAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a foreign key and its foreign auto increment column.

+     */

+    public void testAddFKAndForeignColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true' default='1'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), new Double(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Double(1),  beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Double(1),  beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the addition of a foreign key and its multiple foreign columns.

+     */

+    public void testAddFKAndMultipleForeignColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true' default='1'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the addition of local and foreign column into a foreign key.

+     */

+    public void testAddColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the addition of local and foreign auto increment columns into a foreign key.

+     */

+    public void testAddAutoIncrementColumnIntoFK()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List   beans1  = getRows("roundtrip1");

+        List   beans2  = getRows("roundtrip2");

+        Object avalue2 = ((DynaBean)beans2.get(0)).get("avalue2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Integer(1), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Integer(1).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of local and foreign required columns into a foreign key.

+     */

+    public void testAddRequiredColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the addition of local and foreign columns with default values into a foreign key.

+     */

+    public void testAddColumnsWithDefaultsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true' default='2'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        List   beans1  = getRows("roundtrip1");

+        List   beans2  = getRows("roundtrip2");

+        Object avalue2 = ((DynaBean)beans2.get(0)).get("avalue2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2),  beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        assertTrue((avalue2 == null) || new Double(2).equals(avalue2));

+    }

+

+    /**

+     * Tests the addition of local and foreign required auto increment columns into a foreign key.

+     */

+    public void testAddRequiredAutoIncrementColumnIntoFK()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Integer(1), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        if (InterbasePlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            assertEquals((Object)null, beans2.get(0), "avalue2");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans2.get(0), "avalue2");

+        }

+    }

+

+    /**

+     * Tests the addition of local and foreign required columns with default values into a foreign key.

+     */

+    public void testAddRequiredColumnsWithDefaultsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='CHAR' size='8' primaryKey='true' required='true' default='sometext'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' required='true' default='sometext'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1),     beans1.get(0), "pk1");

+        assertEquals((Object)"sometext", beans1.get(0), "pk2");

+        assertEquals(new Integer(2),     beans2.get(0), "pk");

+        assertEquals(new Integer(1),     beans2.get(0), "avalue1");

+        assertEquals((Object)"sometext", beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the addition of multiple local and foreign columns into a foreign key.

+     */

+    public void testAddMultipleColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' default='1'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='1'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue3' foreign='pk3'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+}

diff --git a/src/test/org/apache/ddlutils/io/TestAlteration.java b/src/test/org/apache/ddlutils/io/TestAlteration.java
index 3a8c4df..30493b7 100644
--- a/src/test/org/apache/ddlutils/io/TestAlteration.java
+++ b/src/test/org/apache/ddlutils/io/TestAlteration.java
@@ -27,11 +27,20 @@
 

 import org.apache.commons.beanutils.DynaBean;

 import org.apache.ddlutils.model.Database;

+import org.apache.ddlutils.platform.mckoi.MckoiPlatform;

+import org.apache.ddlutils.platform.mysql.MySql50Platform;

+import org.apache.ddlutils.platform.mysql.MySqlPlatform;

 import org.apache.ddlutils.platform.sybase.SybasePlatform;

 

 /**

  * Performs tests for the alteration of databases.

- * 

+ *

+ * TODO: add more tests, esp. combining multiple changes

+ *       - change datatype/size and add to/remove from pk

+ *       - change datatype/size and add/remove pk that uses the column

+ *       - change type of column in index and foreign key

+ *       - drop index with columns in a foreign key

+ *       - ...

  * @version $Revision: $

  */

 public class TestAlteration extends RoundtripTestBase

@@ -180,6 +189,56 @@
     }

 

     /**

+     * Tests the alteration of the sizes of PK and FK columns.

+     */

+    public void testChangePKAndFKSizes()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='fk' type='VARCHAR' size='32' required='false'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='fk' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='128' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='fk' type='VARCHAR' size='128' required='false'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='fk' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "test" });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "test" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List     beans = getRows("roundtrip2");

+        DynaBean bean  = (DynaBean)beans.get(0);

+

+        assertEquals((Object)"test", bean, "fk");

+    }

+

+    /**

      * Tests the alteration of the datatypes of columns of a PK and FK that

      * will be dropped.

      */

@@ -686,276 +745,6 @@
     }

 

     /**

-     * Tests the addition of a column.

-     */

-    public void testAddColumn()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-    	alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals((Object)null, beans.get(0), "avalue");

-    }

-

-    /**

-     * Tests the addition of an auto-increment column.

-     */

-    public void testAddAutoIncrementColumn()

-    {

-    	if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

-    	{

-    		return;

-    	}

-

-        // we need special catering for Sybase which does not support identity for INTEGER columns

-        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml;

-

-        if (isSybase)

-        {

-            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-                        "<database name='roundtriptest'>\n"+

-                        "  <table name='roundtrip'>\n"+

-                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-                        "    <column name='avalue' type='NUMERIC' size='12,0' autoIncrement='true' required='true'/>\n"+

-                        "  </table>\n"+

-                        "</database>";

-        }

-        else

-        {

-            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-                        "<database name='roundtriptest'>\n"+

-                        "  <table name='roundtrip'>\n"+

-                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-                        "    <column name='avalue' type='INTEGER' autoIncrement='true' required='true'/>\n"+

-                        "  </table>\n"+

-                        "</database>";

-        }

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-    	alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        if (isSybase)

-        {

-            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

-        }

-        else

-        {

-            Object avalue = ((DynaBean)beans.get(0)).get("avalue");

-

-            assertTrue((avalue == null) || new Integer(1).equals(avalue));

-        }

-    }

-

-    /**

-     * Tests the addition of several columns.

-     */

-    public void testAddColumns()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue3' type='DOUBLE' default='1.0'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

-            "    <column name='avalue2' type='INTEGER'/>\n"+

-            "    <column name='avalue3' type='DOUBLE' default='1.0'/>\n"+

-            "    <column name='avalue4' type='VARCHAR' size='16'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1), new Double(3.0) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals((Object)null, beans.get(0), "avalue1");

-        assertEquals((Object)null, beans.get(0), "avalue2");

-        assertEquals(new Double(3.0), beans.get(0), "avalue3");

-        assertEquals((Object)null, beans.get(0), "avalue4");

-    }

-

-    /**

-     * Tests the addition of several columns at the end of the table.

-     */

-    public void testAddColumnsAtTheEnd()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

-            "    <column name='avalue2' type='INTEGER'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

-            "    <column name='avalue2' type='INTEGER'/>\n"+

-            "    <column name='avalue3' type='DOUBLE' default='1.0'/>\n"+

-            "    <column name='avalue4' type='VARCHAR' size='16'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1), "test", new Integer(3) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals((Object)"test", beans.get(0), "avalue1");

-        assertEquals(new Integer(3), beans.get(0), "avalue2");

-

-        // we cannot be sure whether the default algorithm is used (which will apply the

-        // default value even to existing columns with NULL in it) or the database supports

-        // it dircetly (in which case it might still be NULL)

-        Object avalue3 = ((DynaBean)beans.get(0)).get("avalue3");

-

-        assertTrue((avalue3 == null) || new Double(1.0).equals(avalue3));

-        

-        assertEquals((Object)null, beans.get(0), "avalue4");

-    }

-

-    /**

-     * Tests the addition of a column with a default value. Note that depending

-     * on whether the database supports this via a statement, this test may fail.

-     * For instance, Sql Server has a statement for this which means that the

-     * existing value in column avalue won't be changed and thus the test fails.

-     */

-    public void testAddColumnWithDefault()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='INTEGER' default='2'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        // we cannot be sure whether the default algorithm is used (which will apply the

-        // default value even to existing columns with NULL in it) or the database supports

-        // it dircetly (in which case it might still be NULL)

-        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

-

-        assertTrue((avalue == null) || new Integer(2).equals(avalue));

-    }

-

-    /**

-     * Tests the addition of a column that is set to NOT NULL.

-     */

-    public void testAddRequiredColumn()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='INTEGER' default='2' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals(new Integer(2), beans.get(0), "avalue");

-    }

-

-    /**

      * Tests the change of the order of the columns of a table.

      */

     public void testChangeColumnOrder()

@@ -1163,116 +952,6 @@
     }

 

     /**

-     * Tests the addition of a pk column.

-     */

-    public void testAddPKColumn()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='INTEGER' default='0' primaryKey='true' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-    	alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals(new Integer(0), beans.get(0), "avalue");

-    }

-

-    /**

-     * Tests the addition of a primary key and a column.

-     */

-    public void testAddPKAndColumn()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='INTEGER' default='0'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        // we cannot be sure whether the default algorithm is used (which will apply the

-        // default value even to existing columns with NULL in it) or the database supports

-        // it dircetly (in which case it might still be NULL)

-        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

-

-        assertTrue((avalue == null) || new Integer(0).equals(avalue));

-    }

-

-    /**

-     * Tests the addition of a primary key and a primary key column.

-     */

-    public void testAddPKAndPKColumn()

-    {

-        final String model1Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' required='true'/>\n"+

-            "  </table>\n"+

-            "</database>";

-        final String model2Xml = 

-            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

-            "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

-            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue' type='INTEGER' primaryKey='true' required='true' default='0'/>\n"+

-            "  </table>\n"+

-            "</database>";

-

-        createDatabase(model1Xml);

-

-        insertRow("roundtrip", new Object[] { new Integer(1) });

-

-        alterDatabase(model2Xml);

-

-        assertEquals(getAdjustedModel(),

-                     readModelFromDatabase("roundtriptest"));

-

-        List beans = getRows("roundtrip");

-

-        assertEquals(new Integer(0), beans.get(0), "avalue");

-    }

-

-    /**

      * Tests the removal of a pk column.

      */

     public void testDropPKColumn()

@@ -1443,54 +1122,137 @@
     }

 

     /**

-     * Tests the addition of a column to an index.

+     * Tests the removal of an index that has column that are also used by foreign keys. This is a

+     * test esp. for the handling of http://bugs.mysql.com/bug.php?id=21395.

      */

-    public void testAddColumnToIndex()

+    public void testDropIndexOverlappingWithForeignKeys()

     {

-        if (!getPlatformInfo().isIndicesSupported())

-        {

-            return;

-        }

-

         final String model1Xml = 

             "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

             "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

+            "  <table name='roundtrip1'>\n"+

             "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue1' type='DOUBLE'/>\n"+

-            "    <column name='avalue2' type='VARCHAR' size='40'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='VARCHAR' size='50' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip3'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='50'/>\n"+

             "    <index name='test_index'>\n"+

+            "      <index-column name='avalue2'/>\n"+

             "      <index-column name='avalue1'/>\n"+

             "    </index>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "    <foreign-key foreignTable='roundtrip2'>\n"+

+            "      <reference local='avalue2' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

             "  </table>\n"+

             "</database>";

         final String model2Xml = 

             "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

             "<database name='roundtriptest'>\n"+

-            "  <table name='roundtrip'>\n"+

+            "  <table name='roundtrip1'>\n"+

             "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

-            "    <column name='avalue1' type='DOUBLE'/>\n"+

-            "    <column name='avalue2' type='VARCHAR' size='40'/>\n"+

-            "    <index name='test_index'>\n"+

-            "      <index-column name='avalue1'/>\n"+

-            "      <index-column name='avalue2'/>\n"+

-            "    </index>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='VARCHAR' size='50' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip3'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='50'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "    <foreign-key foreignTable='roundtrip2'>\n"+

+            "      <reference local='avalue2' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

             "  </table>\n"+

             "</database>";

 

         createDatabase(model1Xml);

 

-        insertRow("roundtrip", new Object[] { new Integer(1), new Double(2.0), "test" });

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { "test" });

+        insertRow("roundtrip3", new Object[] { new Integer(1), new Integer(1), "test" });

 

-    	alterDatabase(model2Xml);

+        alterDatabase(model2Xml);

 

         assertEquals(getAdjustedModel(),

                      readModelFromDatabase("roundtriptest"));

 

-        List beans = getRows("roundtrip");

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+        List beans3 = getRows("roundtrip3");

 

-        assertEquals(new Double(2.0), beans.get(0), "avalue1");

-        assertEquals((Object)"test", beans.get(0), "avalue2");

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals((Object)"test", beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans3.get(0), "pk");

+        assertEquals(new Integer(1), beans3.get(0), "avalue1");

+        assertEquals((Object)"test", beans3.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of an index that has column that are also referenced by a remote foreign key. 

+     */

+    public void testDropIndexOverlappingWithRemoteForeignKey()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='50'/>\n"+

+            "    <index name='test_index'>\n"+

+            "      <index-column name='pk'/>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='50'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), "test" });

+        insertRow("roundtrip2", new Object[] { new Integer(1), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals((Object)"test", beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue");

     }

 

     /**

@@ -1650,6 +1412,265 @@
     }

 

     /**

+     * Tests removing a foreign key and an index that has the same name and same column.

+     */

+    public void testDropFKAndCorrespondingIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "    <foreign-key name='test' foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk1'/>\n"+

+            "      <reference local='avalue1' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2.0) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Double(2.0), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2.0), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Double(2.0), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests removing a foreign key but not the index that has the same name and same column.

+     */

+    public void testDropFKButNotCorrespondingIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "    <foreign-key name='test' foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk1'/>\n"+

+            "      <reference local='avalue1' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2.0) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Double(2.0), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2.0), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Double(2.0), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests removing a foreign key and an index that has the same name but different columns.

+     */

+    public void testDropFKAndDifferentIndexWithSameName()

+    {

+        // MySql/InnoDB doesn't allow the creation of a foreign key and index with the same name

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "    <foreign-key name='test' foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk1'/>\n"+

+            "      <reference local='avalue1' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2.0) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Double(2.0), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2.0), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Double(2.0), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests removing a foreign key but not the index that has the same name but different columns.

+     */

+    public void testDropFKButNotDifferentIndexWithSameName()

+    {

+        // MySql/InnoDB doesn't allow the creation of a foreign key and index with the same name

+        if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+            MySql50Platform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "    <foreign-key name='test' foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk1'/>\n"+

+            "      <reference local='avalue1' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2.0) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Double(2.0), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2.0), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Double(2.0), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

      * Tests the removal of several foreign keys. Test for DDLUTILS-150.

      */

     public void testDropFKs()

@@ -2194,6 +2215,24 @@
         assertEquals(getAdjustedModel(),

                      readModelFromDatabase("roundtriptest"));

 

-        assertTrue(getRows("roundtrip").isEmpty());

+        List beans = getRows("roundtrip");

+

+        if (MckoiPlatform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // McKoi can actually handle this, though interestingly it will result in a null value for the pk

+            assertEquals((Object)null,   beans.get(0), "pk");

+            assertEquals((Object)"test", beans.get(0), "avalue");

+        }

+        else if (MySqlPlatform.DATABASENAME.equals(getPlatform().getName()) ||

+                 MySql50Platform.DATABASENAME.equals(getPlatform().getName()))

+        {

+            // Same with MySql except it uses the default value for the datatype

+            assertEquals(new Integer(0), beans.get(0), "pk");

+            assertEquals((Object)"test", beans.get(0), "avalue");

+        }

+        else

+        {

+            assertTrue(beans.isEmpty());

+        }

     }

 }

diff --git a/src/test/org/apache/ddlutils/io/TestDropColumn.java b/src/test/org/apache/ddlutils/io/TestDropColumn.java
new file mode 100644
index 0000000..610fde0
--- /dev/null
+++ b/src/test/org/apache/ddlutils/io/TestDropColumn.java
@@ -0,0 +1,1025 @@
+package org.apache.ddlutils.io;

+

+/*

+ * 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.math.BigDecimal;

+import java.sql.Date;

+import java.util.List;

+

+import junit.framework.Test;

+

+/**

+ * Tests database alterations that drop columns.

+ * 

+ * @version $Revision: $

+ */

+public class TestDropColumn extends RoundtripTestBase

+{

+    /**

+     * Parameterized test case pattern.

+     * 

+     * @return The tests

+     */

+    public static Test suite() throws Exception

+    {

+        return getTests(TestDropColumn.class);

+    }

+

+    /**

+     * Tests the removal of a column.

+     */

+    public void testDropColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='TIMESTAMP'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Date(new java.util.Date().getTime()) });

+

+    	alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of a auto increment column.

+     */

+    public void testDropAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of a required column.

+     */

+    public void testDropRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new BigDecimal(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of a column that has a default value.

+     */

+    public void testDropColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE' default='3.1'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of a required column that has a default value.

+     */

+    public void testDropRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='text'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of multiple columns.

+     */

+    public void testDropMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='CHAR' size='8' default='text'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), null, new Integer(2), new Double(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+        assertEquals(new Integer(2), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of multiple columns, including one with auto incremen.

+     */

+    public void testDropMultipleColumnsInclAutoIncrement()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='CHAR' size='8' default='text'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' required='true'/>\n"+

+            "    <column name='avalue3' type='INTEGER' autoIncrement='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), null, new Double(2), null });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+        assertEquals(new Double(2),  beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of a primary key column.

+     */

+    public void testDropPKColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), new Integer(3) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "pk2");

+        assertEquals(new Integer(3), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the removal of the single primary key column.

+     */

+    public void testDropSinglePKColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the removal of multiple primary key columns.

+     */

+    public void testDropMultiplePKColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), new Integer(3), new Integer(4) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "pk2");

+        assertEquals(new Integer(4), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the removal of all primary key columns.

+     */

+    public void testDropAllPKColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), new Integer(3), new Integer(4) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(4), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the removal of a column from a non-unique index.

+     */

+    public void testDropColumnFromIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), "text" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+        assertEquals((Object)"text", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of the single column from an unique index.

+     */

+    public void testDropSingleColumnFromIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of a column from a non-unique index.

+     */

+    public void testDropMultipleColumnsFromIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue3' type='TIMESTAMP'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue3'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), "text" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+        assertEquals((Object)"text", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of all column from an unique index.

+     */

+    public void testDropAllColumnsFromIndex()

+    {

+        if (!getPlatformInfo().isIndicesSupported())

+        {

+            return;

+        }

+

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "      <unique-column name='avalue3'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2), "text", new Double(2.0) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of the single local column from a foreign key.

+     */

+    public void testDropSingleLocalColumnFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text" });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"text", beans1.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of the single foreign column from a foreign key.

+     */

+    public void testDropSingleForeignColumnFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text" });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(2), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals((Object)"text", beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the removal of all local columns from a foreign key.

+     */

+    public void testDropAllLocalColumnsFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text", new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"text", beans1.get(0), "pk1");

+        assertEquals(new Integer(2), beans1.get(0), "pk2");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+    }

+

+    /**

+     * Tests the removal of all foreign columns from a foreign key.

+     */

+    public void testDropAllForeignColumnsFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2), new Integer(3) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text", new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(3), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals((Object)"text", beans2.get(0), "avalue1");

+        assertEquals(new Integer(2), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of a local and foreign column from a foreign key.

+     */

+    public void testDropLocalAndForeignColumnFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2), new Integer(3) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text", new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(2), beans1.get(0), "pk2");

+        assertEquals(new Integer(3), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of multiple local and foreign columns from a foreign key.

+     */

+    public void testDropMultipleLocalAndForeignColumnsFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue3' foreign='pk3'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2), new Double(4), new Integer(3) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text", new Integer(2), new Double(4) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(2), beans1.get(0), "pk2");

+        assertEquals(new Integer(3), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the removal of all local and foreign columns from a foreign key.

+     */

+    public void testDropAllLocalAndForeignColumnsFromFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue3' foreign='pk3'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text", new Integer(2), new Double(4), new Integer(3) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), "text", new Integer(2), new Double(4) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(3), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+    }

+}

diff --git a/src/test/org/apache/ddlutils/io/TestInsertColumn.java b/src/test/org/apache/ddlutils/io/TestInsertColumn.java
new file mode 100644
index 0000000..6fd8255
--- /dev/null
+++ b/src/test/org/apache/ddlutils/io/TestInsertColumn.java
@@ -0,0 +1,2670 @@
+package org.apache.ddlutils.io;

+

+/*

+ * 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.math.BigDecimal;

+import java.util.List;

+

+import junit.framework.Test;

+

+import org.apache.commons.beanutils.DynaBean;

+import org.apache.ddlutils.platform.sybase.SybasePlatform;

+

+/**

+ * Tests database alterations that insert columns.

+ * 

+ * @version $Revision: $

+ */

+public class TestInsertColumn extends RoundtripTestBase

+{

+    /**

+     * Parameterized test case pattern.

+     * 

+     * @return The tests

+     */

+    public static Test suite() throws Exception

+    {

+        return getTests(TestInsertColumn.class);

+    }

+

+    /**

+     * Tests the insertion of a column.

+     */

+    public void testInsertColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+    	alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an auto-increment column.

+     */

+    public void testInsertAutoIncrementColumn()

+    {

+    	if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+    	{

+    		return;

+    	}

+

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='NUMERIC' size='12,0' autoIncrement='true'/>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+    	alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

+        }

+        else

+        {

+            Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+            assertTrue((avalue == null) || new Integer(1).equals(avalue));

+        }

+    }

+

+    /**

+     * Tests the insertion of a column that is set to NOT NULL.

+     */

+    public void testInsertRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' default='2' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new BigDecimal(2), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insert of a column with a default value. Note that depending

+     * on whether the database supports this via a statement, this test may fail.

+     * For instance, Sql Server has a statement for this which means that the

+     * existing value in column avalue won't be changed and thus the test fails.

+     */

+    public void testInsertColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        // we cannot be sure whether the default algorithm is used (which will apply the

+        // default value even to existing columns with NULL in it) or the database supports

+        // it directly (in which case it might still be NULL)

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || new Double(2).equals(avalue));

+    }

+

+    /**

+     * Tests the insertion of a required auto-increment column.

+     */

+    public void testInsertRequiredAutoIncrementColumn()

+    {

+        if (!getPlatformInfo().isNonPKIdentityColumnsSupported())

+        {

+            return;

+        }

+

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='NUMERIC' size='12,0' autoIncrement='true' required='true'/>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='avalue' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

+        }

+        else

+        {

+            Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+            assertTrue((avalue == null) || new Integer(1).equals(avalue));

+        }

+    }

+

+    /**

+     * Tests the insertion of a column with a default value. Note that depending

+     * on whether the database supports this via a statement, this test may fail.

+     * For instance, Sql Server has a statement for this which means that the

+     * existing value in column avalue won't be changed and thus the test fails.

+     */

+    public void testAddRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='CHAR' size='8' default='text' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        // we cannot be sure whether the default algorithm is used (which will apply the

+        // default value even to existing columns with NULL in it) or the database supports

+        // it directly (in which case it might still be NULL)

+        Object avalue = ((DynaBean)beans.get(0)).get("avalue");

+

+        assertTrue((avalue == null) || "text    ".equals(avalue));

+    }

+

+    /**

+     * Tests the addition and insertion of several columns.

+     */

+    public void testAddAndInsertMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' default='1.0'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue2' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' default='1.0' required='true'/>\n"+

+            "    <column name='avalue4' type='VARCHAR' size='16'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Double(3.0) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue1");

+        assertEquals((Object)null, beans.get(0), "avalue2");

+        assertEquals(new Double(3.0), beans.get(0), "avalue3");

+        assertEquals((Object)null, beans.get(0), "avalue4");

+    }

+

+    /**

+     * Tests the insertion of a primary key and a column.

+     */

+    public void testInsertPKAndColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a primary key and an autoincrement column.

+     */

+    public void testInsertPKAndAutoIncrementColumn()

+    {

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(1), beans.get(0), "avalue");

+        }

+        else

+        {

+            assertEquals(new Integer(1), beans.get(0), "avalue");

+        }

+    }

+

+    /**

+     * Tests the insertion of a primary key and multiple columns.

+     */

+    public void testAddAndInsertPKAndMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+

+    /**

+     * Tests the insertion of a column to a primary key.

+     */

+    public void testInsertColumnIntoPK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of an autoincrement column into the primary key.

+     */

+    public void testInsertAutoIncrementColumnIntoPK()

+    {

+        // we need special catering for Sybase which does not support identity for INTEGER columns

+        boolean      isSybase  = SybasePlatform.DATABASENAME.equals(getPlatform().getName());

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml;

+

+        if (isSybase)

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='pk2' type='NUMERIC' size='12,0' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+        else

+        {

+            model2Xml = "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+                        "<database name='roundtriptest'>\n"+

+                        "  <table name='roundtrip'>\n"+

+                        "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+                        "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+                        "    <column name='avalue' type='INTEGER'/>\n"+

+                        "  </table>\n"+

+                        "</database>";

+        }

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(-1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        if (isSybase)

+        {

+            assertEquals(new BigDecimal(-1), beans.get(0), "pk1");

+        }

+        else

+        {

+            assertEquals(new Integer(-1), beans.get(0), "pk1");

+        }

+        assertEquals(new Integer(2), ((DynaBean)beans.get(0)).get("avalue"));

+    }

+

+    /**

+     * Tests the insertion of multiple columns into the primary key.

+     */

+    public void testInsertMultipleColumnsIntoPK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and a column.

+     */

+    public void testInsertNonUniqueIndexAndColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and an auto increment column.

+     */

+    public void testInsertNonUniqueIndexAndAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and a required column.

+     */

+    public void testInsertNonUniqueIndexAndRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and a column with a default value.

+     */

+    public void testInsertNonUniqueIndexAndColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Double(2), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and a required auto increment column.

+     */

+    public void testInsertNonUniqueIndexAndrequiredAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and a required column with a default value.

+     */

+    public void testInsertNonUniqueIndexAndRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='text'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)"text    ", beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a non-unique index and several columns.

+     */

+    public void testAddAndInsertNonUniqueIndexAndMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+

+    /**

+     * Tests the insertion of an unique index and a column.

+     */

+    public void testInsertUniqueIndexAndColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)null, beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an unique index and an auto increment column.

+     */

+    public void testInsertUniqueIndexAndAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an unique index and a required column.

+     */

+    public void testInsertUniqueIndexAndRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of an unique index and a column with a default value.

+     */

+    public void testInsertUniqueIndexAndColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='DOUBLE' default='2'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Double(2), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an unique index and a required auto increment column.

+     */

+    public void testInsertUniqueIndexAndRequiredAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(1), beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an unique index and a required column with a default value.

+     */

+    public void testInsertUniqueIndexAndRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='text'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals((Object)"text    ", beans.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of an unique index and several columns.

+     */

+    public void testAddAndInsertUniqueIndexAndMultipleColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a column into a non-unique index.

+     */

+    public void testInsertColumnIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals((Object)null,   beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insert of an auto increment column into a non-unique index.

+     */

+    public void testInsertAutoIncrementColumnIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Integer(1), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required column into a non-unique index.

+     */

+    public void testInsertRequiredColumnIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a column with a default value into a non-unique index.

+     */

+    public void testInsertColumnWithDefaultIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Double(2),  beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required auto increment column into a non-unique index.

+     */

+    public void testInsertRequiredAutoIncrementColumnIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Integer(1), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required column with a default value into a non-unique index.

+     */

+    public void testInsertRequiredColumnWithDefaultIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' default='text' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2),     beans.get(0), "avalue1");

+        assertEquals((Object)"text    ", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of multiple columns into a non-unique index.

+     */

+    public void testAddAndInsertMultipleColumnsIntoNonUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='3'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <index name='test'>\n"+

+            "      <index-column name='avalue1'/>\n"+

+            "      <index-column name='avalue2'/>\n"+

+            "      <index-column name='avalue3'/>\n"+

+            "    </index>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a column into an unique index.

+     */

+    public void testInsertColumnIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals((Object)null,   beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of an auto increment column into an unique index.

+     */

+    public void testInsertAutoIncrementColumnIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Integer(1), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required column into an unique index.

+     */

+    public void testInsertRequiredColumnIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a column with a default value into an unique index.

+     */

+    public void testInsertColumnWithDefaultIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Double(2),  beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required auto increment column into an unique index.

+     */

+    public void testInsertRequiredAutoIncrementColumnIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2), beans.get(0), "avalue1");

+        assertEquals(new Integer(1), beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of a required column with a default value into an unique index.

+     */

+    public void testInsertRequiredColumnWithDefaultIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' default='text' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans = getRows("roundtrip");

+

+        assertEquals(new Integer(2),     beans.get(0), "avalue1");

+        assertEquals((Object)"text    ", beans.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of multiple columns into an unique index.

+     */

+    public void testAddAndInsertMultipleColumnsIntoUniqueIndex()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='3'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <unique name='test'>\n"+

+            "      <unique-column name='avalue1'/>\n"+

+            "      <unique-column name='avalue2'/>\n"+

+            "      <unique-column name='avalue3'/>\n"+

+            "    </unique>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip", new Object[] { new Integer(1), new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        assertTrue(getRows("roundtrip").isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local column.

+     */

+    public void testInsertFKAndLocalColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text" });

+        insertRow("roundtrip2", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"text", beans1.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals((Object)null,   beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local auto increment column.

+     */

+    public void testInsertFKAndLocalAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local required column.

+     */

+    public void testInsertFKAndLocalRequiredColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new BigDecimal(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new BigDecimal(1), beans1.get(0), "pk");

+        assertTrue(beans2.isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local column with a default value.

+     */

+    public void testInsertFKAndLocalColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='DOUBLE' default='1'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Double(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Double(1),  beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Double(1),  beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local required auto increment column.

+     */

+    public void testInsertFKAndLocalRequiredAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local required column with a default value.

+     */

+    public void testInsertFKAndLocalRequiredColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='CHAR' size='8' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='CHAR' size='8' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='CHAR' size='8' required='true' default='text'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { "text" });

+        insertRow("roundtrip2", new Object[] { new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals((Object)"text    ", beans1.get(0), "pk");

+        assertEquals(new Integer(1),    beans2.get(0), "pk");

+        assertEquals((Object)"text    ", beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its local columns.

+     */

+    public void testAddAndInsertFKAndMultipleLocalColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue1' type='INTEGER' default='1'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1), new Double(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(3) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2),  beans1.get(0), "pk2");

+        assertTrue(beans2.isEmpty());

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its foreign column.

+     */

+    public void testInsertFKAndForeignColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk' type='VARCHAR' size='32' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue' type='VARCHAR' size='32'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its foreign auto increment column.

+     */

+    public void testInsertFKAndForeignAutoIncrementColumn()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its foreign auto increment column.

+     */

+    public void testInsertFKAndForeignColumnWithDefault()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk' type='DOUBLE' primaryKey='true' required='true' default='1'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue' foreign='pk'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(2) });

+        insertRow("roundtrip2", new Object[] { new Integer(1), new Double(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Double(1),  beans1.get(0), "pk");

+        assertEquals(new Integer(2), beans1.get(0), "avalue");

+        assertEquals(new Integer(1), beans2.get(0), "pk");

+        assertEquals(new Double(1),  beans2.get(0), "avalue");

+    }

+

+    /**

+     * Tests the insertion of a foreign key and its multiple foreign columns.

+     */

+    public void testAddAndInsertFKAndMultipleForeignColumns()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE'/>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' required='true' default='1'/>\n"+

+            "    <column name='avalue' type='INTEGER'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='DOUBLE'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the insertion of local and foreign column into a foreign key.

+     */

+    public void testInsertColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='VARCHAR' size='32' primaryKey='true'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='VARCHAR' size='32'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the insertion of local and foreign auto increment columns into a foreign key.

+     */

+    public void testInsertAutoIncrementColumnIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' autoIncrement='true'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' autoIncrement='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Integer(1), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of local and foreign required columns into a foreign key.

+     */

+    public void testInsertRequiredColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='NUMERIC' size='12,0' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='NUMERIC' size='12,0' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+

+    /**

+     * Tests the insertion of local and foreign columns with default values into a foreign key.

+     */

+    public void testInsertColumnsWithDefaultsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='DOUBLE' primaryKey='true' default='2'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='DOUBLE' default='2'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Double(2),  beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        assertEquals(new Double(2),  beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of local and foreign required auto increment columns into a foreign key.

+     */

+    public void testInsertRequiredAutoIncrementColumnIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' required='true' autoIncrement='true'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='INTEGER' required='true' autoIncrement='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1), beans1.get(0), "pk1");

+        assertEquals(new Integer(1), beans1.get(0), "pk2");

+        assertEquals(new Integer(2), beans2.get(0), "pk");

+        assertEquals(new Integer(1), beans2.get(0), "avalue1");

+        assertEquals(new Integer(1), beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of local and foreign required columns with default values into a foreign key.

+     */

+    public void testInsertRequiredColumnsWithDefaultsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk2' type='CHAR' size='8' primaryKey='true' required='true' default='text'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue2' type='CHAR' size='8' required='true' default='text'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+

+        insertRow("roundtrip1", new Object[] { new Integer(1) });

+        insertRow("roundtrip2", new Object[] { new Integer(2), new Integer(1) });

+

+        alterDatabase(model2Xml);

+

+        List beans1 = getRows("roundtrip1");

+        List beans2 = getRows("roundtrip2");

+

+        assertEquals(new Integer(1),     beans1.get(0), "pk1");

+        assertEquals((Object)"text    ", beans1.get(0), "pk2");

+        assertEquals(new Integer(2),     beans2.get(0), "pk");

+        assertEquals(new Integer(1),     beans2.get(0), "avalue1");

+        assertEquals((Object)"text    ", beans2.get(0), "avalue2");

+    }

+

+    /**

+     * Tests the insertion of multiple local and foreign columns into a foreign key.

+     */

+    public void testAddAndInsertMultipleColumnsIntoFK()

+    {

+        final String model1Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+        final String model2Xml = 

+            "<?xml version='1.0' encoding='ISO-8859-1'?>\n"+

+            "<database name='roundtriptest'>\n"+

+            "  <table name='roundtrip1'>\n"+

+            "    <column name='pk3' type='DOUBLE' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk1' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='pk2' type='INTEGER' primaryKey='true' default='1'/>\n"+

+            "  </table>\n"+

+            "  <table name='roundtrip2'>\n"+

+            "    <column name='avalue3' type='DOUBLE' required='true'/>\n"+

+            "    <column name='pk' type='INTEGER' primaryKey='true' required='true'/>\n"+

+            "    <column name='avalue1' type='INTEGER'/>\n"+

+            "    <column name='avalue2' type='INTEGER' default='1'/>\n"+

+            "    <foreign-key foreignTable='roundtrip1'>\n"+

+            "      <reference local='avalue3' foreign='pk3'/>\n"+

+            "      <reference local='avalue1' foreign='pk1'/>\n"+

+            "      <reference local='avalue2' foreign='pk2'/>\n"+

+            "    </foreign-key>\n"+

+            "  </table>\n"+

+            "</database>";

+

+        createDatabase(model1Xml);

+        // no point trying this with data in the db as it will only cause a constraint violation

+        alterDatabase(model2Xml);

+

+        assertEquals(getAdjustedModel(),

+                     readModelFromDatabase("roundtriptest"));

+    }

+}