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"));
+ }
+}