From d14529b6027d9d6d5e8cf6d96a6004319feb25af Mon Sep 17 00:00:00 2001 From: Roland Praml Date: Wed, 30 Aug 2017 09:54:53 +0200 Subject: [PATCH] FIX: optimized unique constraint handling for platforms that does support them on nullable columns (#1095) --- .../ddlgeneration/platform/BaseTableDdl.java | 32 +++++++++++-------- .../ddlgeneration/platform/DB2Ddl.java | 10 ++++++ .../ddlgeneration/platform/PlatformDdl.java | 13 ++++---- .../ddlgeneration/platform/SqlServerDdl.java | 11 +++++-- 4 files changed, 43 insertions(+), 23 deletions(-) diff --git a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java index 7fb3f6b02..b7798b511 100644 --- a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java +++ b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/BaseTableDdl.java @@ -207,9 +207,12 @@ public class BaseTableDdl implements TableDdl { String tableName = createTable.getName(); for (Column col : externalUnique) { String uqName = col.getUniqueOneToOne(); + if (uqName == null) { + uqName = col.getUnique(); + } String[] columnNames = {col.getName()}; write.apply() - .append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames)) + .append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, Boolean.TRUE.equals(col.isNotnull()))) .endOfStatement(); write.dropAllForeignKeys() @@ -221,7 +224,7 @@ public class BaseTableDdl implements TableDdl { String uqName = constraint.getName(); String[] columnNames = StringHelper.delimitedToArray(constraint.getColumnNames(), ",", false); write.apply() - .append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames)) + .append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames, false)) // TODO: check if nullable .endOfStatement(); write.dropAllForeignKeys() @@ -421,9 +424,9 @@ public class BaseTableDdl implements TableDdl { protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { List uniqueConstraints = createTable.getUniqueConstraint(); - boolean inlineUniqueCompound = platformDdl.isInlineUniqueOneToOne(); + boolean inlineUniqueWhenNull = platformDdl.isInlineUniqueWhenNullable(); for (UniqueConstraint uniqueConstraint : uniqueConstraints) { - if (inlineUniqueCompound) { + if (inlineUniqueWhenNull) { String uqName = uniqueConstraint.getName(); String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames()); apply.append(",").newLine(); @@ -440,17 +443,18 @@ public class BaseTableDdl implements TableDdl { */ protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable) throws IOException { - boolean inlineUniqueOneToOne = platformDdl.isInlineUniqueOneToOne(); + boolean inlineUniqueWhenNullable = platformDdl.isInlineUniqueWhenNullable(); List columns = createTable.getColumn(); for (Column column : columns) { - if (hasValue(column.getUnique()) || (inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne()))) { - // normal mechanism for adding unique constraint - inlineUniqueConstraintSingle(apply, column); - - } else if (!inlineUniqueOneToOne && hasValue(column.getUniqueOneToOne())) { - // MsSqlServer specific mechanism for adding unique constraints (that allow nulls) - externalUnique.add(column); + if (hasValue(column.getUnique()) || hasValue(column.getUniqueOneToOne())) { + if (Boolean.TRUE.equals(column.isNotnull()) || inlineUniqueWhenNullable) { + // normal mechanism for adding unique constraint + inlineUniqueConstraintSingle(apply, column); + } else { + // MsSqlServer & DB2 specific mechanism for adding unique constraints (that allow nulls) + externalUnique.add(column); + } } } } @@ -834,9 +838,9 @@ public class BaseTableDdl implements TableDdl { protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName) throws IOException { String[] cols = {alter.getColumnName()}; - + boolean notNull = alter.isNotnull() != null ? alter.isNotnull() : Boolean.TRUE.equals(alter.isNotnull()); writer.apply() - .append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols)) + .append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols, notNull)) .endOfStatement(); writer.dropAllForeignKeys() diff --git a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/DB2Ddl.java b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/DB2Ddl.java index 9dae8e8a6..ae15e85ea 100644 --- a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/DB2Ddl.java +++ b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/DB2Ddl.java @@ -10,6 +10,16 @@ public class DB2Ddl extends PlatformDdl { public DB2Ddl(DatabasePlatform platform) { super(platform); this.identitySuffix = " generated by default as identity"; + this.inlineUniqueWhenNullable = false; + } + + public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) { + if (notNull) { + return super.alterTableAddUniqueConstraint(tableName, uqName, columns, true); + } else { + // Hmm: Complex workaround: https://www.ibm.com/developerworks/mydeveloperworks/blogs/SQLTips4DB2LUW/entry/unique_where_not_null_indexes26?lang=en + return "-- NOT SUPPORTED " + super.alterTableAddUniqueConstraint(tableName, uqName, columns, true); + } } } diff --git a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java index d3bd0175f..258e5bec6 100644 --- a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java +++ b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/PlatformDdl.java @@ -91,7 +91,7 @@ public class PlatformDdl { /** * Set false for MsSqlServer to allow multiple nulls for OneToOne mapping. */ - protected boolean inlineUniqueOneToOne = true; + protected boolean inlineUniqueWhenNullable = true; protected DbConstraintNaming naming; @@ -371,7 +371,7 @@ public class PlatformDdl { *

* Overridden by MsSqlServer for specific null handling on unique constraints. */ - public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns) { + public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) { StringBuilder buffer = new StringBuilder(90); buffer.append("alter table ").append(tableName).append(" add constraint ").append(uqName).append(" unique "); @@ -401,11 +401,12 @@ public class PlatformDdl { } /** - * Return true if unique constraints for OneToOne can be inlined as normal. - * Returns false for MsSqlServer due to it's null handling for unique constraints. + * Return true if unique constraints for nullable columns can be inlined as normal. + * Returns false for MsSqlServer & DB2 due to it's not possible to to put a constraint + * on a nullable column */ - public boolean isInlineUniqueOneToOne() { - return inlineUniqueOneToOne; + public boolean isInlineUniqueWhenNullable() { + return inlineUniqueWhenNullable; } /** diff --git a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/SqlServerDdl.java b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/SqlServerDdl.java index de5854eab..02dfe516a 100644 --- a/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/SqlServerDdl.java +++ b/src/main/java/io/ebean/dbmigration/ddlgeneration/platform/SqlServerDdl.java @@ -17,7 +17,7 @@ public class SqlServerDdl extends PlatformDdl { this.foreignKeyRestrict = ""; this.alterTableIfExists = ""; this.addColumn = "add"; - this.inlineUniqueOneToOne = false; + this.inlineUniqueWhenNullable = false; this.columnSetDefault = "add default"; this.dropConstraintIfExists = "drop constraint"; this.historyDdl = new SqlServerHistoryDdl(); @@ -51,8 +51,13 @@ public class SqlServerDdl extends PlatformDdl { * MsSqlServer specific null handling on unique constraints. */ @Override - public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns) { - + public String alterTableAddUniqueConstraint(String tableName, String uqName, String[] columns, boolean notNull) { + if (notNull) { + return super.alterTableAddUniqueConstraint(tableName, uqName, columns, notNull); + } + if (uqName == null) { + throw new NullPointerException(); + } // issues#233 String start = "create unique nonclustered index " + uqName + " on " + tableName + "("; StringBuilder sb = new StringBuilder(start);