From 6b4d6106f686586d735dabe8af5af173df88b3d1 Mon Sep 17 00:00:00 2001 From: Philip Marzullo Date: Mon, 4 Feb 2019 15:32:39 -0500 Subject: [PATCH] 0003537: initial load create: Cascade Delete rule is not syncing as part of the create DDL --- .../org/jumpmind/db/io/DatabaseXmlUtil.java | 41 ++- .../org/jumpmind/db/model/ForeignKey.java | 97 ++++- .../db/platform/AbstractDdlBuilder.java | 34 ++ .../db/platform/ase/AseDdlBuilder.java | 6 + .../db/platform/db2/Db2DdlBuilder.java | 10 + .../db/platform/derby/DerbyDdlBuilder.java | 18 + .../platform/firebird/FirebirdDdlBuilder.java | 24 ++ .../greenplum/GreenplumDdlBuilder.java | 7 + .../platform/informix/InformixDdlBuilder.java | 19 +- .../interbase/InterbaseDdlBuilder.java | 24 ++ .../platform/mssql/MsSql2000DdlBuilder.java | 23 ++ .../db/platform/nuodb/NuoDbDdlBuilder.java | 7 + .../db/platform/oracle/OracleDdlBuilder.java | 18 +- .../db/platform/raima/RaimaDdlBuilder.java | 18 + .../platform/redshift/RedshiftDdlBuilder.java | 7 + .../sqlanywhere/SqlAnywhereDdlBuilder.java | 23 ++ .../db/platform/tibero/TiberoDdlBuilder.java | 16 + .../db/platform/voltdb/VoltDbDdlBuilder.java | 7 + .../db/platform/AbstractDdlBuilderTest.java | 345 ++++++++++++++++++ .../db/platform/AbstractJdbcDdlReader.java | 21 ++ 20 files changed, 759 insertions(+), 6 deletions(-) diff --git a/symmetric-db/src/main/java/org/jumpmind/db/io/DatabaseXmlUtil.java b/symmetric-db/src/main/java/org/jumpmind/db/io/DatabaseXmlUtil.java index be2cf357a6..156669da00 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/io/DatabaseXmlUtil.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/io/DatabaseXmlUtil.java @@ -41,6 +41,7 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.ForeignKey; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.IndexColumn; import org.jumpmind.db.model.NonUniqueIndex; @@ -275,6 +276,10 @@ public static Table nextTable(XmlPullParser parser, String catalog, String schem fk.setForeignTableCatalog(attributeValue); } else if (attributeName.equalsIgnoreCase("foreignTableSchema")) { fk.setForeignTableSchema(attributeValue); + } else if (attributeName.equalsIgnoreCase("foreignOnUpdateAction")) { + fk.setOnUpdateAction(ForeignKey.getForeignKeyActionByForeignKeyActionName(attributeValue)); + } else if (attributeName.equalsIgnoreCase("foreignOnDeleteAction")) { + fk.setOnDeleteAction(ForeignKey.getForeignKeyActionByForeignKeyActionName(attributeValue)); } } table.addForeignKey(fk); @@ -523,7 +528,13 @@ public static void write(Table table, Writer output) { + StringEscapeUtils.escapeXml(fk.getForeignTableCatalog() == null || fk.getForeignTableCatalog().equals(table.getCatalog()) ? "" : fk.getForeignTableCatalog()) + "\" foreignTableSchema=\"" + StringEscapeUtils.escapeXml(fk.getForeignTableSchema() == null || - fk.getForeignTableSchema().equals(table.getSchema()) ? "" : fk.getForeignTableSchema()) + "\">\n"); + fk.getForeignTableSchema().equals(table.getSchema()) ? "" : fk.getForeignTableSchema()) + "\"" + + + writeForeignKeyOnUpdateClause(fk) + + + writeForeignKeyOnDeleteClause(fk) + + + ">\n"); for (Reference ref : fk.getReferences()) { output.write("\t\t\t it = references.iterator(); it.hasNext();) { result.references.add(((Reference) it.next()).clone()); @@ -324,6 +352,8 @@ public boolean equals(Object obj) { builder.append(name, otherFk.name); } builder.append(foreignTableName, otherFk.foreignTableName); + builder.append(getOnDeleteAction(), otherFk.getOnDeleteAction()); + builder.append(getOnUpdateAction(), otherFk.getOnUpdateAction()); builder.append(references.size(), otherFk.references.size()); for (int i = 0; i < references.size() && i < otherFk.references.size(); i++) { @@ -337,7 +367,7 @@ public boolean equals(Object obj) { } } - /** + /** * Compares this foreign key to the given one while ignoring the case of * identifiers. * @@ -352,6 +382,12 @@ public boolean equalsIgnoreCase(ForeignKey otherFk) { if ((!checkName || name.equalsIgnoreCase(otherFk.name)) && foreignTableName.equalsIgnoreCase(otherFk.foreignTableName)) { + if(! otherFk.getOnDeleteAction().equals(getOnDeleteAction())) { + return false; + } + if(! otherFk.getOnUpdateAction().equals(getOnUpdateAction())) { + return false; + } HashSet otherRefs = new HashSet(); otherRefs.addAll(otherFk.references); @@ -392,6 +428,8 @@ public int hashCode() { if (isNotBlank(name)) { builder.append(name); } + builder.append(getOnDeleteAction()); + builder.append(getOnUpdateAction()); return builder.toHashCode(); } @@ -410,6 +448,12 @@ public String toString() { result.append("foreign table="); result.append(getForeignTableName()); result.append("; "); + if(! getOnDeleteAction().equals(ForeignKeyAction.UNDEFINED)) { + result.append("ON DELETE " + getOnDeleteAction().getForeignKeyActionName()).append("; "); + } + if(! getOnUpdateAction().equals(ForeignKeyAction.UNDEFINED)) { + result.append("ON UPDATE " + getOnUpdateAction().getForeignKeyActionName()).append(";"); + } result.append(getReferenceCount()); result.append(" references]"); @@ -424,7 +468,7 @@ public String toString() { public String toVerboseString() { StringBuffer result = new StringBuffer(); - result.append("ForeignK ky ["); + result.append("Foreign key ["); if ((getName() != null) && (getName().length() > 0)) { result.append("name="); result.append(getName()); @@ -432,6 +476,13 @@ public String toVerboseString() { } result.append("foreign table="); result.append(getForeignTableName()); + result.append(";"); + if(! getOnDeleteAction().equals(ForeignKeyAction.UNDEFINED)) { + result.append(" ON DELETE " + getOnDeleteAction().getForeignKeyActionName()); + } + if(! getOnUpdateAction().equals(ForeignKeyAction.UNDEFINED)) { + result.append(" ON UPDATE " + getOnUpdateAction().getForeignKeyActionName()); + } result.append("] references:"); for (int idx = 0; idx < getReferenceCount(); idx++) { result.append(" "); @@ -457,6 +508,46 @@ public void setForeignTableSchema(String foreignTableSchema) { this.foreignTableSchema = foreignTableSchema; } + public ForeignKeyAction getOnDeleteAction() { + return onDeleteAction; + } + + public void setOnDeleteAction(ForeignKeyAction onDeleteAction) { + this.onDeleteAction = onDeleteAction; + } + + public ForeignKeyAction getOnUpdateAction() { + return onUpdateAction; + } + + public void setOnUpdateAction(ForeignKeyAction onUpdateAction) { + this.onUpdateAction = onUpdateAction; + } + + public static ForeignKeyAction getForeignKeyAction(short importedKeyAction) { + switch(importedKeyAction) { + case DatabaseMetaData.importedKeyCascade: + return ForeignKeyAction.CASCADE; + case DatabaseMetaData.importedKeyNoAction: + return ForeignKeyAction.NOACTION; + case DatabaseMetaData.importedKeyRestrict: + return ForeignKeyAction.RESTRICT; + case DatabaseMetaData.importedKeySetDefault: + return ForeignKeyAction.SETDEFAULT; + case DatabaseMetaData.importedKeySetNull: + return ForeignKeyAction.SETNULL; + default: + return ForeignKeyAction.UNDEFINED; + } + } - + public static ForeignKeyAction getForeignKeyActionByForeignKeyActionName(String foreignKeyActionName) throws IllegalArgumentException { + for(ForeignKeyAction action : ForeignKeyAction.values()) { + if(StringUtils.equals(foreignKeyActionName, action.getForeignKeyActionName())) { + return action; + } + } + throw new IllegalArgumentException("Unknown ForeignKeyAction: " + foreignKeyActionName); + } + } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDdlBuilder.java index 07149e52a5..e83d656d1a 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/AbstractDdlBuilder.java @@ -58,6 +58,7 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.ForeignKey; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.IndexColumn; import org.jumpmind.db.model.ModelException; @@ -2398,6 +2399,7 @@ protected void writeEmbeddedForeignKeysStmt(Table table, StringBuilder ddl) { ddl.append(" ("); writeForeignReferences(key, ddl); ddl.append(")"); + writeCascadeAttributesForForeignKey(key, ddl); } } } @@ -2449,9 +2451,41 @@ protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ddl.append(" ("); writeForeignReferences(key, ddl); ddl.append(")"); + writeCascadeAttributesForForeignKey(key, ddl); printEndOfStatement(ddl); } } + + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + writeCascadeAttributesForForeignKeyDelete(key, ddl); + writeCascadeAttributesForForeignKeyUpdate(key, ddl); + } + + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // No need to output action for RESTRICT and NO ACTION since that is the default in every database that supports foreign keys + if(! ( + key.getOnDeleteAction().equals(ForeignKeyAction.UNDEFINED) || + key.getOnDeleteAction().equals(ForeignKeyAction.RESTRICT) || + key.getOnDeleteAction().equals(ForeignKeyAction.NOACTION) + ) + ) + { + ddl.append(" ON DELETE " + key.getOnDeleteAction().getForeignKeyActionName()); + } + } + + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // No need to output action for RESTRICT and NO ACTION since that is the default in every database that supports foreign keys + if(! ( + key.getOnUpdateAction().equals(ForeignKeyAction.UNDEFINED) || + key.getOnUpdateAction().equals(ForeignKeyAction.RESTRICT) || + key.getOnUpdateAction().equals(ForeignKeyAction.NOACTION) + ) + ) + { + ddl.append(" ON UPDATE " + key.getOnUpdateAction().getForeignKeyActionName()); + } + } /** * Writes a list of local references for the given foreign key. diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/ase/AseDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/ase/AseDdlBuilder.java index fd0dc7a0cb..ec06758c30 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/ase/AseDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/ase/AseDdlBuilder.java @@ -577,4 +577,10 @@ protected void processColumnChange(Table sourceTable, Table targetTable, Column protected String createUniqueIdentifier() { return new UID().toString().replace(':', '_').replace('-', '_'); } + + @Override + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + // Sybase does not support cascade actions + return; + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/db2/Db2DdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/db2/Db2DdlBuilder.java index 6503eeaefe..63fc4fcd46 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/db2/Db2DdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/db2/Db2DdlBuilder.java @@ -33,9 +33,11 @@ import org.jumpmind.db.alter.TableChange; import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; import org.jumpmind.db.model.TypeMap; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -297,4 +299,12 @@ protected void writeReorgStmt(Table table, StringBuilder ddl) { ddl.append(getFullyQualifiedTableNameShorten(table)); ddl.append("')"); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // DB2 only supports RESTRICT and NO ACTION for ON UPDATE + if(key.getOnUpdateAction().equals(ForeignKeyAction.RESTRICT) || key.getOnUpdateAction().equals(ForeignKeyAction.NOACTION)) { + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + } + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/derby/DerbyDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/derby/DerbyDdlBuilder.java index 410ed1962c..38e39b6d7a 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/derby/DerbyDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/derby/DerbyDdlBuilder.java @@ -28,9 +28,11 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.ColumnTypes; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; import org.jumpmind.db.model.TypeMap; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -169,4 +171,20 @@ protected void processChange(Database currentModel, Database desiredModel, change.apply(currentModel, delimitedIdentifierModeOn); } + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Derby does not support ON UPDATE SET DEFAULT + if(! key.getOnUpdateAction().equals(ForeignKeyAction.SETDEFAULT)) { + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + } + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Derby does not support ON DELETE SET DEFAULT + if(! key.getOnDeleteAction().equals(ForeignKeyAction.SETDEFAULT)) { + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + } + } + } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/firebird/FirebirdDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/firebird/FirebirdDdlBuilder.java index 52920e4e72..33fd3cfe45 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/firebird/FirebirdDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/firebird/FirebirdDdlBuilder.java @@ -34,8 +34,10 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.ColumnTypes; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -410,4 +412,26 @@ protected void processChange(Database currentModel, Database desiredModel, printEndOfStatement(ddl); change.apply(currentModel, delimitedIdentifierModeOn); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Firebird does not support ON UPDATE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnUpdateAction(); + if(key.getOnUpdateAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnUpdateAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + key.setOnUpdateAction(original); + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Firebird does not support ON DELETE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnDeleteAction(); + if(key.getOnDeleteAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnDeleteAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + key.setOnDeleteAction(original); + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/greenplum/GreenplumDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/greenplum/GreenplumDdlBuilder.java index b41ee4b6ae..a89c6b9cd9 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/greenplum/GreenplumDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/greenplum/GreenplumDdlBuilder.java @@ -20,6 +20,7 @@ */ package org.jumpmind.db.platform.greenplum; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.platform.postgresql.PostgreSqlDdlBuilder; public class GreenplumDdlBuilder extends PostgreSqlDdlBuilder { @@ -27,4 +28,10 @@ public class GreenplumDdlBuilder extends PostgreSqlDdlBuilder { public GreenplumDdlBuilder() { databaseInfo.setTriggersSupported(false); } + + @Override + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + // Greenplum does not support cascade actions + return; + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/informix/InformixDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/informix/InformixDdlBuilder.java index e5bfe82040..21c2a5b72b 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/informix/InformixDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/informix/InformixDdlBuilder.java @@ -29,6 +29,7 @@ import org.jumpmind.db.model.Database; import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; @@ -122,11 +123,27 @@ protected void writeExternalForeignKeyCreateStmt(Database database, Table table, printIdentifier(getTableName(key.getForeignTableName()), ddl); ddl.append(" ("); writeForeignReferences(key, ddl); - ddl.append(") CONSTRAINT "); + ddl.append(")"); + writeCascadeAttributesForForeignKey(key, ddl); + ddl.append(" CONSTRAINT "); printIdentifier(getForeignKeyName(table, key), ddl); printEndOfStatement(ddl); } } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Informix does not support ON UPDATE + return; + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Informix only supports ON DELETE CASCADE + if(key.getOnDeleteAction().equals(ForeignKeyAction.CASCADE)) { + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + } + } protected void processChange(Database currentModel, Database desiredModel, RemovePrimaryKeyChange change, StringBuilder ddl) { diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/interbase/InterbaseDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/interbase/InterbaseDdlBuilder.java index 08414ed0d5..04f695a900 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/interbase/InterbaseDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/interbase/InterbaseDdlBuilder.java @@ -33,8 +33,10 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.ColumnTypes; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -381,4 +383,26 @@ protected void processChange(Database currentModel, Database desiredModel, printEndOfStatement(ddl); change.apply(currentModel, delimitedIdentifierModeOn); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Interbase does not support ON UPDATE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnUpdateAction(); + if(key.getOnUpdateAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnUpdateAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + key.setOnUpdateAction(original); + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Interbase does not support ON DELETE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnDeleteAction(); + if(key.getOnDeleteAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnDeleteAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + key.setOnDeleteAction(original); + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DdlBuilder.java index d420203eac..1c06ad7863 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/mssql/MsSql2000DdlBuilder.java @@ -72,6 +72,7 @@ import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; import org.jumpmind.db.model.TypeMap; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -754,4 +755,26 @@ protected void filterColumnSqlType(StringBuilder sqlType) { sqlType.append("nvarbinary(max)"); } } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // MSSQL does not support ON UPDATE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnUpdateAction(); + if(key.getOnUpdateAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnUpdateAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + key.setOnUpdateAction(original); + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // MSSQL does not support ON DELETE RESTRICT, but RESTRICT is just like NOACTION + ForeignKeyAction original = key.getOnDeleteAction(); + if(key.getOnDeleteAction().equals(ForeignKeyAction.RESTRICT)) { + key.setOnDeleteAction(ForeignKeyAction.NOACTION); + } + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + key.setOnDeleteAction(original); + } } \ No newline at end of file diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/nuodb/NuoDbDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/nuodb/NuoDbDdlBuilder.java index 65e00838dc..eb134837db 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/nuodb/NuoDbDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/nuodb/NuoDbDdlBuilder.java @@ -206,6 +206,7 @@ protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ddl.append(" ("); writeForeignReferences(key, ddl); ddl.append(")"); + writeCascadeAttributesForForeignKey(key, ddl); printEndOfStatement(ddl); } } @@ -296,6 +297,12 @@ protected void processColumnChange(Table sourceTable, Table targetTable, Column protected void writeColumnNullableStmt(StringBuilder ddl) { } + @Override + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + // NuoDB does not support cascade actions + return; + } + @Override protected String getSqlType(Column column) { String sqlType = super.getSqlType(column); diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/oracle/OracleDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/oracle/OracleDdlBuilder.java index 167e285d05..d53f62e4db 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/oracle/OracleDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/oracle/OracleDdlBuilder.java @@ -39,9 +39,11 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.ColumnTypes; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.PlatformColumn; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -554,6 +556,20 @@ protected String getSqlType(Column column) { } else { return super.getSqlType(column); } - } + } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Oracle does not support ON UPDATE + return; + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Oracle only supports CASCADE and SET NULL + if(key.getOnDeleteAction().equals(ForeignKeyAction.CASCADE) || key.getOnDeleteAction().equals(ForeignKeyAction.SETNULL)) { + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + } + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/raima/RaimaDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/raima/RaimaDdlBuilder.java index 407121cc81..5fe6599634 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/raima/RaimaDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/raima/RaimaDdlBuilder.java @@ -34,6 +34,7 @@ import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; @@ -212,6 +213,7 @@ protected void writeExternalForeignKeyCreateStmt(Database database, Table table, ddl.append(" ("); writeForeignReferences(key, ddl); ddl.append(")"); + writeCascadeAttributesForForeignKey(key, ddl); printEndOfStatement(ddl); } } @@ -297,5 +299,21 @@ protected void processColumnChange(Table sourceTable, Table targetTable, Column } printEndOfStatement(ddl); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Raima does not support ON UPDATE SET DEFAULT + if(! key.getOnUpdateAction().equals(ForeignKeyAction.SETDEFAULT)) { + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + } + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Raima does not support ON DELETE SET DEFAULT + if(! key.getOnDeleteAction().equals(ForeignKeyAction.SETDEFAULT)) { + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + } + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/redshift/RedshiftDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/redshift/RedshiftDdlBuilder.java index 626cf92d43..c9544a8d88 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/redshift/RedshiftDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/redshift/RedshiftDdlBuilder.java @@ -23,6 +23,7 @@ import java.sql.Types; import org.jumpmind.db.model.Column; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.Table; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; @@ -66,5 +67,11 @@ public RedshiftDdlBuilder() { protected void writeColumnAutoIncrementStmt(Table table, Column column, StringBuilder ddl) { } + + @Override + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + // Redshift does not support cascade actions + return; + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlBuilder.java index 463d0bdd84..bdf4a89f7c 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/sqlanywhere/SqlAnywhereDdlBuilder.java @@ -43,6 +43,7 @@ import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -495,4 +496,26 @@ protected void processColumnChange(Table sourceTable, Table targetTable, Column protected String createUniqueIdentifier() { return new UID().toString().replace(':', '_').replace('-', '_'); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // SQLAnywhere does not support ON UPDATE NO ACTION, but NOACTION is just like RESTRICT + ForeignKeyAction original = key.getOnUpdateAction(); + if(key.getOnUpdateAction().equals(ForeignKeyAction.NOACTION)) { + key.setOnUpdateAction(ForeignKeyAction.RESTRICT); + } + super.writeCascadeAttributesForForeignKeyUpdate(key, ddl); + key.setOnUpdateAction(original); + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Firebird does not support ON DELETE NO ACTION, but NOACTION is just like RESTRICT + ForeignKeyAction original = key.getOnDeleteAction(); + if(key.getOnDeleteAction().equals(ForeignKeyAction.NOACTION)) { + key.setOnDeleteAction(ForeignKeyAction.RESTRICT); + } + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + key.setOnDeleteAction(original); + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/tibero/TiberoDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/tibero/TiberoDdlBuilder.java index 70369e28aa..8e2dccc56a 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/tibero/TiberoDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/tibero/TiberoDdlBuilder.java @@ -20,8 +20,10 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.ColumnTypes; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.Table; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.platform.AbstractDdlBuilder; import org.jumpmind.db.platform.DatabaseNamesConstants; import org.jumpmind.db.platform.PlatformUtils; @@ -508,5 +510,19 @@ protected void processChange(Database currentModel, Database desiredModel, printEndOfStatement(ddl); change.apply(currentModel, delimitedIdentifierModeOn); } + + @Override + protected void writeCascadeAttributesForForeignKeyUpdate(ForeignKey key, StringBuilder ddl) { + // Tibero does not support ON UPDATE + return; + } + + @Override + protected void writeCascadeAttributesForForeignKeyDelete(ForeignKey key, StringBuilder ddl) { + // Tibero only supports CASCADE and SET NULL + if(key.getOnDeleteAction().equals(ForeignKeyAction.CASCADE) || key.getOnDeleteAction().equals(ForeignKeyAction.SETNULL)) { + super.writeCascadeAttributesForForeignKeyDelete(key, ddl); + } + } } diff --git a/symmetric-db/src/main/java/org/jumpmind/db/platform/voltdb/VoltDbDdlBuilder.java b/symmetric-db/src/main/java/org/jumpmind/db/platform/voltdb/VoltDbDdlBuilder.java index 19d7fc98a6..6c2ecf2d13 100644 --- a/symmetric-db/src/main/java/org/jumpmind/db/platform/voltdb/VoltDbDdlBuilder.java +++ b/symmetric-db/src/main/java/org/jumpmind/db/platform/voltdb/VoltDbDdlBuilder.java @@ -27,6 +27,7 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.IndexColumn; import org.jumpmind.db.model.Table; @@ -153,5 +154,11 @@ protected void alignIndexColumns(IIndex currentIndex, IIndex desiredIndex) { currentIndex.addColumn(currentColumn); } } + + @Override + protected void writeCascadeAttributesForForeignKey(ForeignKey key, StringBuilder ddl) { + // VoltDB does not support cascade actions + return; + } } diff --git a/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDdlBuilderTest.java b/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDdlBuilderTest.java index 2bbc2107a5..24f4b9268a 100644 --- a/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDdlBuilderTest.java +++ b/symmetric-db/src/test/java/org/jumpmind/db/platform/AbstractDdlBuilderTest.java @@ -23,23 +23,297 @@ import static org.junit.Assert.assertTrue; import java.sql.Types; +import java.util.Arrays; import org.jumpmind.db.model.Column; +import org.jumpmind.db.model.Database; +import org.jumpmind.db.model.ForeignKey; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.model.PlatformColumn; +import org.jumpmind.db.model.Reference; import org.jumpmind.db.model.Table; +import org.jumpmind.db.platform.ase.AseDdlBuilder; +import org.jumpmind.db.platform.db2.Db2As400DdlBuilder; +import org.jumpmind.db.platform.db2.Db2DdlBuilder; +import org.jumpmind.db.platform.derby.DerbyDdlBuilder; +import org.jumpmind.db.platform.firebird.FirebirdDdlBuilder; +import org.jumpmind.db.platform.greenplum.GreenplumDdlBuilder; import org.jumpmind.db.platform.h2.H2DdlBuilder; +import org.jumpmind.db.platform.hsqldb.HsqlDbDdlBuilder; +import org.jumpmind.db.platform.hsqldb2.HsqlDb2DdlBuilder; +import org.jumpmind.db.platform.informix.InformixDdlBuilder; +import org.jumpmind.db.platform.interbase.InterbaseDdlBuilder; +import org.jumpmind.db.platform.mssql.MsSql2000DdlBuilder; +import org.jumpmind.db.platform.mssql.MsSql2005DdlBuilder; +import org.jumpmind.db.platform.mssql.MsSql2008DdlBuilder; +import org.jumpmind.db.platform.mysql.MySqlDdlBuilder; +import org.jumpmind.db.platform.nuodb.NuoDbDdlBuilder; import org.jumpmind.db.platform.oracle.OracleDdlBuilder; import org.jumpmind.db.platform.postgresql.PostgreSqlDdlBuilder; +import org.jumpmind.db.platform.raima.RaimaDdlBuilder; +import org.jumpmind.db.platform.redshift.RedshiftDdlBuilder; +import org.jumpmind.db.platform.sqlanywhere.SqlAnywhereDdlBuilder; +import org.jumpmind.db.platform.sqlite.SqliteDdlBuilder; +import org.jumpmind.db.platform.tibero.TiberoDdlBuilder; +import org.jumpmind.db.platform.voltdb.VoltDbDdlBuilder; import org.junit.Before; import org.junit.Test; public class AbstractDdlBuilderTest { AbstractDdlBuilder[] ddlBuilders; + + DdlBuilderForeignKeySupport[] foreignKeyDdlBuilders; + + private Database database; + + public class DdlBuilderForeignKeySupport { + private AbstractDdlBuilder ddlBuilder; + private ForeignKeyAction[] onDeleteForeignKeyAction; + private ForeignKeyAction[] onUpdateForeignKeyAction; + private boolean supportsOnDelete; + private boolean supportsOnUpdate; + + public DdlBuilderForeignKeySupport(AbstractDdlBuilder ddlBuilder, boolean supportsOnDelete, ForeignKeyAction[] onDeleteForeignKeyAction, boolean supportsOnUpdate, ForeignKeyAction[] onUpdateForeignKeyAction) { + this.ddlBuilder = ddlBuilder; + this.supportsOnDelete = supportsOnDelete; + this.onDeleteForeignKeyAction = onDeleteForeignKeyAction; + this.supportsOnUpdate = supportsOnUpdate; + this.onUpdateForeignKeyAction = onUpdateForeignKeyAction; + } + + public AbstractDdlBuilder getDdlBuilder() {return ddlBuilder;} + public ForeignKeyAction[] getOnDeleteForeignKeyAction() {return onDeleteForeignKeyAction;} + public ForeignKeyAction[] getOnUpdateForeignKeyAction() {return onUpdateForeignKeyAction;} + public boolean isSupportsOnDelete() {return supportsOnDelete; } + public boolean isSupportsOnUpdate() {return supportsOnUpdate; } + } @Before public void setup() { ddlBuilders = new AbstractDdlBuilder[] { new H2DdlBuilder(), new OracleDdlBuilder(), new PostgreSqlDdlBuilder() }; + buildForeignKeyDdlBuilders(); + buildForeignKeyTables(); + } + + private void buildForeignKeyTables() { + database = new Database(); + + Column t1c1 = new Column("id1",true, Types.VARCHAR, 5, 0); + Table t1 = new Table("t1", t1c1); + database.addTable(t1); + + Column t2c1 = new Column("id2", true, Types.VARCHAR, 5, 0); + Column t2c2 = new Column("id1", false, Types.VARCHAR, 5, 0); + Table t2 = new Table("t2", t2c1, t2c2); + ForeignKey fk2 = new ForeignKey(); + fk2.setForeignTable(t1); + fk2.setOnDeleteAction(ForeignKeyAction.CASCADE); + fk2.setOnUpdateAction(ForeignKeyAction.CASCADE); + Reference r2 = new Reference(t2c2, t1c1); + fk2.addReference(r2); + t2.addForeignKey(fk2); + database.addTable(t2); + + Column t3c1 = new Column("id3", true, Types.VARCHAR, 5, 0); + Table t3 = new Table("t3", t3c1); + database.addTable(t3); + + Column t4c1 = new Column("id4", true, Types.VARCHAR, 5, 0); + Column t4c2 = new Column("id3", false, Types.VARCHAR, 5, 0); + Table t4 = new Table("t4", t4c1, t4c2); + ForeignKey fk4 = new ForeignKey(); + fk4.setForeignTable(t3); + fk4.setOnUpdateAction(ForeignKeyAction.RESTRICT); + fk4.setOnDeleteAction(ForeignKeyAction.RESTRICT); + Reference r4 = new Reference(t4c2, t3c1); + fk4.addReference(r4); + t4.addForeignKey(fk4); + database.addTable(t4); + + Column t5c1 = new Column("id5", true, Types.VARCHAR, 5, 0); + Table t5 = new Table("t5", t5c1); + database.addTable(t5); + + Column t6c1 = new Column("id6", true, Types.VARCHAR, 5, 0); + Column t6c2 = new Column("id5", false, Types.VARCHAR, 5, 0); + Table t6 = new Table("t6", t6c1, t6c2); + ForeignKey fk6 = new ForeignKey(); + fk6.setForeignTable(t5); + fk6.setOnDeleteAction(ForeignKeyAction.NOACTION); + fk6.setOnUpdateAction(ForeignKeyAction.NOACTION); + Reference r6 = new Reference(t6c2, t5c1); + fk6.addReference(r6); + t6.addForeignKey(fk6); + database.addTable(t6); + + Column t7c1 = new Column("id7", true, Types.VARCHAR, 5, 0); + Table t7 = new Table("t7", t7c1); + database.addTable(t7); + + Column t8c1 = new Column("id8", true, Types.VARCHAR, 5, 0); + Column t8c2 = new Column("id7", false, Types.VARCHAR, 5, 0); + Table t8 = new Table("t8", t8c1, t8c2); + ForeignKey fk8 = new ForeignKey(); + fk8.setForeignTable(t7); + fk8.setOnDeleteAction(ForeignKeyAction.SETDEFAULT); + fk8.setOnUpdateAction(ForeignKeyAction.SETDEFAULT); + Reference r8 = new Reference(t8c2, t7c1); + fk8.addReference(r8); + t8.addForeignKey(fk8); + database.addTable(t8); + + Column t9c1 = new Column("id9", true, Types.VARCHAR, 5, 0); + Table t9 = new Table("t9", t9c1); + database.addTable(t9); + + Column t10c1 = new Column("id10", true, Types.VARCHAR, 5, 0); + Column t10c2 = new Column("id9", false, Types.VARCHAR, 5, 0); + Table t10 = new Table("t10", t10c1, t10c2); + ForeignKey fk10 = new ForeignKey(); + fk10.setForeignTable(t9); + fk10.setOnDeleteAction(ForeignKeyAction.SETNULL); + fk10.setOnUpdateAction(ForeignKeyAction.SETNULL); + Reference r10 = new Reference(t10c2, t9c1); + fk10.addReference(r10); + t10.addForeignKey(fk10); + database.addTable(t10); + } + + private void buildForeignKeyDdlBuilders() { + foreignKeyDdlBuilders = new DdlBuilderForeignKeySupport[] { + // Sybase + new DdlBuilderForeignKeySupport(new AseDdlBuilder(), + false,new ForeignKeyAction[] {}, + false,new ForeignKeyAction[] {}), + // DB2 AS400 + new DdlBuilderForeignKeySupport(new Db2As400DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION}), + // DB2 + new DdlBuilderForeignKeySupport(new Db2DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION}), + // Derby + new DdlBuilderForeignKeySupport(new DerbyDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETNULL}), + // Firebird + new DdlBuilderForeignKeySupport(new FirebirdDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // Greenplum + new DdlBuilderForeignKeySupport(new GreenplumDdlBuilder(), + false,new ForeignKeyAction[] {}, + false,new ForeignKeyAction[] {}), + // H2 + new DdlBuilderForeignKeySupport(new H2DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // HsqlDB + new DdlBuilderForeignKeySupport(new HsqlDbDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // HsqlDB2 + new DdlBuilderForeignKeySupport(new HsqlDb2DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // Informix + new DdlBuilderForeignKeySupport(new InformixDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE}, + false,new ForeignKeyAction[] {}), + // Interbase + new DdlBuilderForeignKeySupport(new InterbaseDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // MSsql 2000 + new DdlBuilderForeignKeySupport(new MsSql2000DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // MSsql 2005 + new DdlBuilderForeignKeySupport(new MsSql2005DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // MSsql 2008 + new DdlBuilderForeignKeySupport(new MsSql2008DdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // MySql + new DdlBuilderForeignKeySupport(new MySqlDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // NuoDb + new DdlBuilderForeignKeySupport(new NuoDbDdlBuilder(), + false,new ForeignKeyAction[] {}, + false,new ForeignKeyAction[] {}), + // Oracle + new DdlBuilderForeignKeySupport(new OracleDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.SETNULL}, + false,new ForeignKeyAction[] {}), + // Postgres + new DdlBuilderForeignKeySupport(new PostgreSqlDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // Raima + new DdlBuilderForeignKeySupport(new RaimaDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETNULL}), + // Redshift + new DdlBuilderForeignKeySupport(new RedshiftDdlBuilder(), + false,new ForeignKeyAction[] {}, + false,new ForeignKeyAction[] {}), + // SqlAnywhere + new DdlBuilderForeignKeySupport(new SqlAnywhereDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // Sqlite + new DdlBuilderForeignKeySupport(new SqliteDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}, + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.RESTRICT,ForeignKeyAction.NOACTION,ForeignKeyAction.SETDEFAULT,ForeignKeyAction.SETNULL}), + // Tibero + new DdlBuilderForeignKeySupport(new TiberoDdlBuilder(), + true,new ForeignKeyAction[] { + ForeignKeyAction.CASCADE,ForeignKeyAction.SETNULL}, + false,new ForeignKeyAction[] {}), + // VoltDb + new DdlBuilderForeignKeySupport(new VoltDbDdlBuilder(), + false,new ForeignKeyAction[] {}, + false,new ForeignKeyAction[] {}) + }; } @Test @@ -78,5 +352,76 @@ public void testAlterTableWithNewVarcharSizeForPlatformWithoutPlatformColumn() t } } + @Test + public void testForeignKeySupport() throws Exception { + for(DdlBuilderForeignKeySupport dbfs : foreignKeyDdlBuilders) { + String ddl = dbfs.getDdlBuilder().createTables(database, false); + if(dbfs.isSupportsOnDelete()) { + if(Arrays.asList(dbfs.getOnDeleteForeignKeyAction()).contains(ForeignKeyAction.CASCADE)) { + assertTrue("Failed to generate ON DELETE CASCADE for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON DELETE CASCADE")); + } else { + assertTrue("Failed to generate ddl without ON DELETE CASCADE for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON DELETE CASCADE")); + } + + // RESTRICT is always removed + assertTrue("Failed to generate ddl without ON DELETE RESTRICT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON DELETE RESTRICT")); + + // NO ACTION is always removed + assertTrue("Failed to generate ddl without ON DELETE NO ACTION for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON DELETE NO ACTION")); + + if(Arrays.asList(dbfs.getOnDeleteForeignKeyAction()).contains(ForeignKeyAction.SETDEFAULT)) { + assertTrue("Failed to generate ON DELETE SET DEFAULT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON DELETE SET DEFAULT")); + } else { + assertTrue("Failed to generate ddl without ON DELETE SET DEFAULT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON DELETE SET DEFAULT")); + } + + if(Arrays.asList(dbfs.getOnDeleteForeignKeyAction()).contains(ForeignKeyAction.SETNULL)) { + assertTrue("Failed to generate ON DELETE SET NULL for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON DELETE SET NULL")); + } else { + assertTrue("Failed to generate ddl without ON DELETE SET NULL for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON DELETE SET NULL")); + } + } + if(dbfs.isSupportsOnUpdate()) { + if(Arrays.asList(dbfs.getOnUpdateForeignKeyAction()).contains(ForeignKeyAction.CASCADE)) { + assertTrue("Failed to generate ON UPDATE CASCADE for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON UPDATE CASCADE")); + } else { + assertTrue("Failed to generate ddl without ON UPDATE CASCADE for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON UPDATE CASCADE")); + } + + // RESTRICT is always removed + assertTrue("Failed to generate ddl without ON UPDATE RESTRICT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON UPDATE RESTRICT")); + + // NO ACTION is always removed + assertTrue("Failed to generate ddl without ON UPDATE NO ACTION for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON UPDATE NO ACTION")); + + if(Arrays.asList(dbfs.getOnUpdateForeignKeyAction()).contains(ForeignKeyAction.SETDEFAULT)) { + assertTrue("Failed to generate ON UPDATE SET DEFAULT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON UPDATE SET DEFAULT")); + } else { + assertTrue("Failed to generate ddl without ON UPDATE SET DEFAULT for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON UPDATE SET DEFAULT")); + } + if(Arrays.asList(dbfs.getOnUpdateForeignKeyAction()).contains(ForeignKeyAction.SETNULL)) { + assertTrue("Failed to generate ON UPDATE SET NULL for the following platform: " + + dbfs.getDdlBuilder().databaseName, ddl.contains(" ON UPDATE SET NULL")); + } else { + assertTrue("Failed to generate ddl without ON UPDATE SET NULL for the following platform: " + + dbfs.getDdlBuilder().databaseName, ! ddl.contains("ON UPDATE SET NULL")); + } + } + } + } } diff --git a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/AbstractJdbcDdlReader.java b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/AbstractJdbcDdlReader.java index 00fc7b2bf3..43e809bde1 100644 --- a/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/AbstractJdbcDdlReader.java +++ b/symmetric-jdbc/src/main/java/org/jumpmind/db/platform/AbstractJdbcDdlReader.java @@ -46,6 +46,7 @@ import org.jumpmind.db.model.Column; import org.jumpmind.db.model.Database; import org.jumpmind.db.model.ForeignKey; +import org.jumpmind.db.model.ForeignKey.ForeignKeyAction; import org.jumpmind.db.model.IIndex; import org.jumpmind.db.model.IndexColumn; import org.jumpmind.db.model.NonUniqueIndex; @@ -262,6 +263,8 @@ protected List initColumnsForFK() { result.add(new MetaDataColumnDescriptor(getName("FKTABLE_NAME"), Types.VARCHAR)); result.add(new MetaDataColumnDescriptor(getName("PKCOLUMN_NAME"), Types.VARCHAR)); result.add(new MetaDataColumnDescriptor(getName("FKCOLUMN_NAME"), Types.VARCHAR)); + result.add(new MetaDataColumnDescriptor(getName("UPDATE_RULE"), Types.TINYINT)); + result.add(new MetaDataColumnDescriptor(getName("DELETE_RULE"), Types.TINYINT)); return result; } @@ -1090,6 +1093,8 @@ protected void readForeignKey(DatabaseMetaDataWrapper metaData, Map values, ForeignKey fk) { + if(values.get(getName("UPDATE_RULE")) != null && values.get(getName("UPDATE_RULE")) instanceof Short) { + fk.setOnUpdateAction(ForeignKey.getForeignKeyAction((Short) values.get(getName("UPDATE_RULE")))); + } else { + fk.setOnUpdateAction(ForeignKeyAction.UNDEFINED); + } + } + + protected void readForeignKeyDeleteRule(Map values, ForeignKey fk) { + if(values.get(getName("DELETE_RULE")) != null && values.get(getName("DELETE_RULE")) instanceof Short) { + fk.setOnDeleteAction(ForeignKey.getForeignKeyAction((Short) values.get(getName("DELETE_RULE")))); + } else { + fk.setOnDeleteAction(ForeignKeyAction.UNDEFINED); + } + } /* * Retrieves the foreign keys that reference the indicated table.