From 2fb533a60675aac7d4985171d1d239b20ffa762e Mon Sep 17 00:00:00 2001 From: rob bygrave Date: Thu, 12 Sep 2019 20:34:37 +1200 Subject: [PATCH] #1819 - ENH: For DB Migration support dbMigration.setAddForeignKeySkipCheck(true); --- pom.xml | 2 +- .../io/ebean/dbmigration/DbMigration.java | 9 ++++++ .../dbmigration/DefaultDbMigration.java | 13 ++++++-- .../dbmigration/ddlgeneration/DdlOptions.java | 31 +++++++++++++++++++ .../dbmigration/ddlgeneration/DdlWrite.java | 14 +++++++-- .../ddlgeneration/platform/BaseTableDdl.java | 12 +++---- .../ddlgeneration/platform/ClickHouseDdl.java | 3 +- .../ddlgeneration/platform/PlatformDdl.java | 8 ++++- .../ddlgeneration/platform/PostgresDdl.java | 1 + .../ddlgeneration/platform/SQLiteDdl.java | 3 +- .../dbmigration/model/CurrentModel.java | 9 +++++- .../ddlgeneration/BaseDdlHandlerTest.java | 2 +- .../platform/H2HistoryDdlTest.java | 3 +- .../ModelBuild_explicitSequencesTest.java | 5 +-- .../pg-apply.sql | 2 +- 15 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlOptions.java diff --git a/pom.xml b/pom.xml index a460fc600..516a231e7 100644 --- a/pom.xml +++ b/pom.xml @@ -9,7 +9,7 @@ io.ebean ebean - 11.44.2-SNAPSHOT + 11.45.1-SNAPSHOT jar ebean diff --git a/src/main/java/io/ebean/dbmigration/DbMigration.java b/src/main/java/io/ebean/dbmigration/DbMigration.java index 7880945f9..5319719ba 100644 --- a/src/main/java/io/ebean/dbmigration/DbMigration.java +++ b/src/main/java/io/ebean/dbmigration/DbMigration.java @@ -136,6 +136,14 @@ public interface DbMigration { */ void setGeneratePendingDrop(String generatePendingDrop); + /** + * Set to true if ALTER TABLE ADD FOREIGN KEY should be generated with an option to skip validation. + *

+ * Currently this is only useful for Postgres DDL adding the NOT VALID option. + *

+ */ + void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck); + /** * Add an additional platform to write the migration DDL. *

@@ -198,6 +206,7 @@ public interface DbMigration { * An "init" migration can only be executed and used on a database that has had no * prior migrations run on it. *

+ * * @return the version of the generated migration */ String generateInitMigration() throws IOException; diff --git a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java index 671e9f844..2c2016e8c 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java +++ b/src/main/java/io/ebeaninternal/dbmigration/DefaultDbMigration.java @@ -26,6 +26,7 @@ import io.ebean.config.dbplatform.sqlserver.SqlServer17Platform; import io.ebean.dbmigration.DbMigration; import io.ebean.migration.MigrationVersion; import io.ebeaninternal.api.SpiEbeanServer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.migration.Migration; import io.ebeaninternal.dbmigration.migrationreader.MigrationXmlWriter; @@ -106,6 +107,7 @@ public class DefaultDbMigration implements DbMigration { protected String version; protected String name; protected String generatePendingDrop; + private boolean addForeignKeySkipCheck; protected boolean includeBuiltInPartitioning = true; @@ -180,6 +182,11 @@ public class DefaultDbMigration implements DbMigration { this.name = name; } + @Override + public void setAddForeignKeySkipCheck(boolean addForeignKeySkipCheck) { + this.addForeignKeySkipCheck = addForeignKeySkipCheck; + } + @Override public void setGeneratePendingDrop(String generatePendingDrop) { this.generatePendingDrop = generatePendingDrop; @@ -565,7 +572,8 @@ public class DefaultDbMigration implements DbMigration { } else if (databasePlatform != null) { // writer needs the current model to provide table/column details for // history ddl generation (triggers, history tables etc) - DdlWrite write = new DdlWrite(new MConfiguration(), request.current); + DdlOptions options = new DdlOptions(addForeignKeySkipCheck); + DdlWrite write = new DdlWrite(new MConfiguration(), request.current, options); PlatformDdlWriter writer = createDdlWriter(databasePlatform); writer.processMigration(dbMigration, write, request.migrationDir, fullVersion); } @@ -623,8 +631,9 @@ public class DefaultDbMigration implements DbMigration { */ private void writeExtraPlatformDdl(String fullVersion, CurrentModel currentModel, Migration dbMigration, File writePath) throws IOException { + DdlOptions options = new DdlOptions(addForeignKeySkipCheck); for (Pair pair : platforms) { - DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read()); + DdlWrite platformBuffer = new DdlWrite(new MConfiguration(), currentModel.read(), options); PlatformDdlWriter platformWriter = createDdlWriter(pair.platform); File subPath = platformWriter.subPath(writePath, pair.prefix); platformWriter.processMigration(dbMigration, platformBuffer, subPath, fullVersion); diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlOptions.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlOptions.java new file mode 100644 index 000000000..d1af35458 --- /dev/null +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlOptions.java @@ -0,0 +1,31 @@ +package io.ebeaninternal.dbmigration.ddlgeneration; + +/** + * Options used when generated DDL. + */ +public class DdlOptions { + + private boolean foreignKeySkipCheck; + + public DdlOptions(boolean foreignKeySkipCheck) { + this.foreignKeySkipCheck = foreignKeySkipCheck; + } + + public DdlOptions() { + } + + /** + * Return true if ADD FOREIGN KEY should use a skip check option. + */ + public boolean isForeignKeySkipCheck() { + return foreignKeySkipCheck; + } + + /** + * Set to true if ADD FOREIGN KEY should use a skip check option. + */ + public void setForeignKeySkipCheck(boolean foreignKeySkipCheck) { + this.foreignKeySkipCheck = foreignKeySkipCheck; + } + +} diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java index 163323fbb..0f37f3374 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/DdlWrite.java @@ -26,17 +26,19 @@ public class DdlWrite { private final DdlBuffer dropAll; + private final DdlOptions options; + /** * Create without any configuration or current model (no history support). */ public DdlWrite() { - this(new MConfiguration(), new ModelContainer()); + this(new MConfiguration(), new ModelContainer(), new DdlOptions()); } /** * Create with a configuration. */ - public DdlWrite(MConfiguration configuration, ModelContainer currentModel) { + public DdlWrite(MConfiguration configuration, ModelContainer currentModel, DdlOptions options) { this.currentModel = currentModel; this.applyDropDependencies = new BaseDdlBuffer(configuration); this.apply = new BaseDdlBuffer(configuration); @@ -45,6 +47,14 @@ public class DdlWrite { this.applyHistoryTrigger = new BaseDdlBuffer(configuration); this.dropAllForeignKeys = new BaseDdlBuffer(configuration); this.dropAll = new BaseDdlBuffer(configuration); + this.options = options; + } + + /** + * Return the DDL options. + */ + public DdlOptions getOptions() { + return options; } /** diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java index ff6745913..e0e9265a0 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -7,6 +7,7 @@ import io.ebean.config.dbplatform.DbHistorySupport; import io.ebean.config.dbplatform.IdType; import io.ebean.util.StringHelper; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.ddlgeneration.TableDdl; import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.IndexSet; @@ -31,7 +32,6 @@ import io.ebeaninternal.dbmigration.model.MTable; import java.io.IOException; import java.math.BigInteger; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; @@ -462,7 +462,7 @@ public class BaseTableDdl implements TableDdl { fkeyBuffer.appendStatement(platformDdl.createIndex(request.indexName(), tableName, request.cols())); } - alterTableAddForeignKey(fkeyBuffer, request); + alterTableAddForeignKey(write.getOptions(), fkeyBuffer, request); fkeyBuffer.end(); write.dropAllForeignKeys().appendStatement(platformDdl.alterTableDropForeignKey(tableName, request.fkName())); @@ -473,9 +473,9 @@ public class BaseTableDdl implements TableDdl { write.dropAllForeignKeys().end(); } - protected void alterTableAddForeignKey(DdlBuffer buffer, WriteForeignKey request) throws IOException { + protected void alterTableAddForeignKey(DdlOptions options, DdlBuffer buffer, WriteForeignKey request) throws IOException { - buffer.appendStatement(platformDdl.alterTableAddForeignKey(request)); + buffer.appendStatement(platformDdl.alterTableAddForeignKey(options, request)); } protected void appendColumns(String[] columns, DdlBuffer buffer) throws IOException { @@ -635,7 +635,7 @@ public class BaseTableDdl implements TableDdl { if (DdlHelp.isDropForeignKey(alterForeignKey.getColumnNames())) { writer.apply().appendStatement(platformDdl.alterTableDropForeignKey(alterForeignKey.getTableName(), alterForeignKey.getName())); } else { - writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(new WriteForeignKey(alterForeignKey))); + writer.apply().appendStatement(platformDdl.alterTableAddForeignKey(writer.getOptions(), new WriteForeignKey(alterForeignKey))); } } @@ -899,7 +899,7 @@ public class BaseTableDdl implements TableDdl { protected void alterColumnAddForeignKey(DdlWrite writer, AlterColumn alterColumn) throws IOException { - alterTableAddForeignKey(writer.apply(), new WriteForeignKey(alterColumn)); + alterTableAddForeignKey(writer.getOptions(), writer.apply(), new WriteForeignKey(alterColumn)); } protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException { diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java index bb96d613a..3f4c46d71 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/ClickHouseDdl.java @@ -4,6 +4,7 @@ import io.ebean.config.ServerConfig; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import java.io.IOException; @@ -40,7 +41,7 @@ public class ClickHouseDdl extends PlatformDdl { } @Override - public String alterTableAddForeignKey(WriteForeignKey request) { + public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) { return null; } diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java index c1b71aae3..209df5276 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -11,6 +11,7 @@ import io.ebean.util.StringHelper; import io.ebeaninternal.dbmigration.ddlgeneration.BaseDdlHandler; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.PlatformTypeConverter; import io.ebeaninternal.dbmigration.ddlgeneration.platform.util.VowelRemover; @@ -106,6 +107,8 @@ public class PlatformDdl { protected String dropColumnSuffix = ""; + protected String addForeignKeySkipCheck = ""; + /** * Set false for MsSqlServer to allow multiple nulls for OneToOne mapping. */ @@ -405,7 +408,7 @@ public class PlatformDdl { /** * Add foreign key. */ - public String alterTableAddForeignKey(WriteForeignKey request) { + public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) { StringBuilder buffer = new StringBuilder(90); buffer @@ -418,6 +421,9 @@ public class PlatformDdl { .append(lowerTableName(request.refTable())); appendColumns(request.refCols(), buffer); appendForeignKeySuffix(request, buffer); + if (options.isForeignKeySkipCheck()) { + buffer.append(addForeignKeySkipCheck); + } return buffer.toString(); } diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java index 5230f14a0..56d307892 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/PostgresDdl.java @@ -17,6 +17,7 @@ public class PostgresDdl extends PlatformDdl { this.columnSetType = "type "; this.alterTableIfExists = "if exists "; this.columnSetNull = "drop not null"; + this.addForeignKeySkipCheck = " not valid"; } @Override diff --git a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java index bd2b41830..392051dac 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java +++ b/src/main/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/SQLiteDdl.java @@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebean.config.dbplatform.DatabasePlatform; import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; /** * DB2 platform specific DDL. @@ -25,7 +26,7 @@ public class SQLiteDdl extends PlatformDdl { } @Override - public String alterTableAddForeignKey(WriteForeignKey request) { + public String alterTableAddForeignKey(DdlOptions options, WriteForeignKey request) { // not supported return null; } diff --git a/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java b/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java index 811bad5c9..d09dfbc54 100644 --- a/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java +++ b/src/main/java/io/ebeaninternal/dbmigration/model/CurrentModel.java @@ -3,6 +3,7 @@ package io.ebeaninternal.dbmigration.model; import io.ebean.config.DbConstraintNaming; import io.ebeaninternal.api.SpiEbeanServer; import io.ebeaninternal.dbmigration.ddlgeneration.DdlHandler; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.ddlgeneration.platform.DefaultConstraintMaxLength; import io.ebeaninternal.dbmigration.migration.ChangeSet; @@ -37,6 +38,8 @@ public class CurrentModel { private DdlWrite write; + private DdlOptions ddlOptions = new DdlOptions(); + /** * Construct with a given EbeanServer instance for DDL create all generation, not migration. */ @@ -63,6 +66,10 @@ public class CurrentModel { this.jaxbPresent = server.getServerConfig().getClassLoadConfig().isJavaxJAXBPresent(); } + public DdlOptions getDdlOptions() { + return ddlOptions; + } + /** * Return true if the model contains tables that are partitioned. */ @@ -183,7 +190,7 @@ public class CurrentModel { if (write == null) { ChangeSet createChangeSet = getChangeSet(); - write = new DdlWrite(new MConfiguration(), model); + write = new DdlWrite(new MConfiguration(), model, ddlOptions); DdlHandler handler = handler(); handler.generateProlog(write); diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java index bfb7b0b64..dccc292fa 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/BaseDdlHandlerTest.java @@ -141,7 +141,7 @@ public class BaseDdlHandlerTest extends BaseTestCase { write = new DdlWrite(); hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnIntegerWithLength()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array(90));\n\n"); - + write = new DdlWrite(); hanaHandler().generate(write, Helper.getAlterTableAddDbArrayColumnInteger()); assertThat(write.apply().getBuffer()).isEqualTo("alter table foo add ( dbarray_integer integer array);\n\n"); diff --git a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java index ce7e11d59..f424ddd8b 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/ddlgeneration/platform/H2HistoryDdlTest.java @@ -2,6 +2,7 @@ package io.ebeaninternal.dbmigration.ddlgeneration.platform; import io.ebean.Ebean; import io.ebean.config.dbplatform.h2.H2Platform; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; import io.ebeaninternal.dbmigration.model.CurrentModel; import io.ebeaninternal.dbmigration.model.MConfiguration; @@ -27,7 +28,7 @@ public class H2HistoryDdlTest { CurrentModel currentModel = new CurrentModel(ebeanServer); ModelContainer modelContainer = currentModel.read(); - DdlWrite write = new DdlWrite(new MConfiguration(), modelContainer); + DdlWrite write = new DdlWrite(new MConfiguration(), modelContainer, new DdlOptions()); H2Platform h2Platform = new H2Platform(); PlatformDdl h2Ddl = PlatformDdlBuilder.create(h2Platform); diff --git a/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuild_explicitSequencesTest.java b/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuild_explicitSequencesTest.java index c2db773ee..4ffeb7d6f 100644 --- a/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuild_explicitSequencesTest.java +++ b/src/test/java/io/ebeaninternal/dbmigration/model/build/ModelBuild_explicitSequencesTest.java @@ -4,6 +4,7 @@ package io.ebeaninternal.dbmigration.model.build; import io.ebean.BaseTestCase; import io.ebean.EbeanServerFactory; import io.ebean.config.ServerConfig; +import io.ebeaninternal.dbmigration.ddlgeneration.DdlOptions; import io.ebeaninternal.dbmigration.ddlgeneration.Helper; import io.ebeaninternal.dbmigration.model.CurrentModel; import io.ebeaninternal.api.SpiEbeanServer; @@ -55,13 +56,13 @@ public class ModelBuild_explicitSequencesTest extends BaseTestCase { SpiEbeanServer ebeanServer = getServer(true); CurrentModel currentModel = new CurrentModel(ebeanServer); + final DdlOptions ddlOptions = currentModel.getDdlOptions(); + ddlOptions.setForeignKeySkipCheck(true); String apply = currentModel.getCreateDdl(); assertThat(apply) .startsWith("-- Generated by ebean") .endsWith(Helper.asText(this, "/assert/ModelBuild_explicitSequencesTest/pg-apply.sql")); - } - } diff --git a/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql b/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql index 329da196f..94d31e04f 100644 --- a/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql +++ b/src/test/resources/assert/ModelBuild_explicitSequencesTest/pg-apply.sql @@ -14,5 +14,5 @@ create table phones ( ); create index ix_phones_person_id on phones (person_id); -alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict; +alter table phones add constraint fk_phones_person_id foreign key (person_id) references persons (id) on delete restrict on update restrict not valid;