Featur/dbmigration 3 (#1118)

* ADD: more test classes for Softdelete and History

* update reference models after adding test classes

* DbMigration:Improved History generation for sqlserver

* FIX: When DB supports Sql2011 platform, we must not alter the history-table (as this is handled by DB itself)

* DbMigration: When dropping a table, drop the sequence also.

* DbMigration: dropColumn is handled by platformDdl now, as SqlServerDdl needs to delete dependent objects (default constraints...) first

* FIX: DbMigration is not executed on history table

* DbMigration: can drop comment now.

* No effective code change: completed ObjectFactory / fixed imports

* FIX: drop table comment

* Committed the reference models
This commit is contained in:
Roland Praml
2017-09-10 15:26:01 +12:00
committed by Rob Bygrave
parent 84d3578e33
commit 9a7646195c
60 changed files with 922 additions and 45 deletions
@@ -3,6 +3,8 @@ package io.ebean.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.NamingConvention;
import io.ebean.config.ServerConfig;
import io.ebean.config.dbplatform.DbHistorySupport;
import io.ebean.config.dbplatform.DbIdentity;
import io.ebean.config.dbplatform.IdType;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
@@ -74,6 +76,8 @@ public class BaseTableDdl implements TableDdl {
protected Map<String, HistoryTableUpdate> regenerateHistoryTriggers = new LinkedHashMap<>();
private boolean strict;
private final boolean sql2011History;
/**
* Helper class that is used to execute the migration ddl before and after the migration action.
@@ -196,6 +200,8 @@ public class BaseTableDdl implements TableDdl {
this.platformDdl = platformDdl;
this.platformDdl.configure(serverConfig);
this.strict = true; // TODO RPr serverConfig.getMigrationConfig().isStrict();
DbHistorySupport hist = platformDdl.getPlatform().getHistorySupport();
this.sql2011History = hist != null && hist.isStandardsBased();
}
/**
@@ -512,6 +518,14 @@ public class BaseTableDdl implements TableDdl {
buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
}
/**
* Add 'drop sequence' statement to the buffer.
*/
protected void dropSequence(DdlBuffer buffer, String sequenceName) throws IOException {
buffer.append(platformDdl.dropSequence(sequenceName)).endOfStatement();
}
/**
* Write all the check constraints.
*/
@@ -715,7 +729,7 @@ public class BaseTableDdl implements TableDdl {
alterTableAddColumn(writer.apply(), tableName, column, false);
}
if (isTrue(addColumn.isWithHistory())) {
if (isTrue(addColumn.isWithHistory()) && !sql2011History) {
// make same changes to the history table
String historyTable = historyTable(tableName);
for (Column column : columns) {
@@ -741,6 +755,15 @@ public class BaseTableDdl implements TableDdl {
public void generate(DdlWrite writer, DropTable dropTable) throws IOException {
dropTable(writer.apply(), dropTable.getName());
if (hasValue(dropTable.getSequenceCol())
&& platformDdl.getPlatform().getDbIdentity().isSupportsSequence()) {
String sequenceName = dropTable.getSequenceName();
if (!hasValue(sequenceName)) {
sequenceName = namingConvention.getSequenceName(dropTable.getName(), dropTable.getSequenceCol());
}
dropSequence(writer.apply(), sequenceName);
}
}
/**
@@ -752,7 +775,7 @@ public class BaseTableDdl implements TableDdl {
String tableName = dropColumn.getTableName();
alterTableDropColumn(writer.apply(), tableName, dropColumn.getColumnName());
if (isTrue(dropColumn.isWithHistory())) {
if (isTrue(dropColumn.isWithHistory()) && !sql2011History) {
// also drop from the history table
regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.DROP, dropColumn.getColumnName());
alterTableDropColumn(writer.apply(), historyTable(tableName), dropColumn.getColumnName());
@@ -853,7 +876,7 @@ public class BaseTableDdl implements TableDdl {
if (hasValue(ddl)) {
writer.apply().append(ddl).endOfStatement();
if (isTrue(alter.isWithHistory()) && alter.getType() != null) {
if (isTrue(alter.isWithHistory()) && alter.getType() != null && !sql2011History) {
// mysql and sql server column type change allowing nulls in the history table column
AlterColumn alterHistoryColumn = new AlterColumn();
alterHistoryColumn.setTableName(historyTable(alter.getTableName()));
@@ -904,7 +927,7 @@ public class BaseTableDdl implements TableDdl {
String ddl = platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
if (hasValue(ddl)) {
writer.apply().append(ddl).endOfStatement();
if (isTrue(alter.isWithHistory())) {
if (isTrue(alter.isWithHistory()) && !sql2011History) {
// apply same type change to matching column in the history table
ddl = platformDdl.alterColumnType(historyTable(alter.getTableName()), alter.getColumnName(), alter.getType());
writer.apply().append(ddl).endOfStatement();
@@ -970,17 +993,20 @@ public class BaseTableDdl implements TableDdl {
protected void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("alter table ").append(tableName).append(" drop column ").append(columnName)
.endOfStatement();
platformDdl.alterTableDropColumn(buffer, tableName, columnName);
}
protected void alterTableAddColumn(DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable) throws IOException {
DdlMigrationHelp help = new DdlMigrationHelp(tableName, column);
help.writeBefore(buffer);
if (!onHistoryTable) {
help.writeBefore(buffer);
}
platformDdl.alterTableAddColumn(buffer, tableName, column, onHistoryTable, help.getDefaultValue());
help.writeAfter(buffer);
if (!onHistoryTable) {
help.writeAfter(buffer);
}
}
protected boolean isFalse(Boolean value) {
@@ -418,6 +418,11 @@ public class PlatformDdl {
}
}
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("alter table ").append(tableName).append(" drop column ").append(columnName)
.endOfStatement();
}
/**
* Return true if unique constraints for nullable columns can be inlined as normal.
@@ -542,15 +547,19 @@ public class PlatformDdl {
* Add table comment as a separate statement (from the create table statement).
*/
public void addTableComment(DdlBuffer apply, String tableName, String tableComment) throws IOException {
if (DdlHelp.isDropComment(tableComment)) {
tableComment = "";
}
apply.append(String.format("comment on table %s is '%s'", tableName, tableComment)).endOfStatement();
}
/**
* Add column comment as a separate statement.
*/
public void addColumnComment(DdlBuffer apply, String table, String column, String comment) throws IOException {
if (DdlHelp.isDropComment(comment)) {
comment = "";
}
apply.append(String.format("comment on column %s.%s is '%s'", table, column, comment)).endOfStatement();
}
}
@@ -25,7 +25,12 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public String dropTable(String tableName) {
return "IF OBJECT_ID('" + tableName + "', 'U') IS NOT NULL drop table " + tableName;
StringBuilder buffer = new StringBuilder();
buffer.append("IF OBJECT_ID('");
buffer.append(tableName);
buffer.append("', 'U') IS NOT NULL drop table ");
buffer.append(tableName);
return buffer.toString();
}
@Override
@@ -76,7 +81,24 @@ public class SqlServerDdl extends PlatformDdl {
}
return sb.toString();
}
public String alterTableDropConstraint(String tableName, String constraintName) {
StringBuilder sb = new StringBuilder();
sb.append("IF (OBJECT_ID('").append(constraintName).append("', 'C') IS NOT NULL) ");
sb.append(super.alterTableDropConstraint(tableName, constraintName));
return sb.toString();
}
/**
* Drop a unique constraint from the table (Sometimes this is an index).
*/
@Override
public String alterTableDropUniqueConstraint(String tableName, String uniqueConstraintName) {
StringBuilder sb = new StringBuilder();
sb.append("IF (OBJECT_ID('").append(uniqueConstraintName).append("', 'UQ') IS NOT NULL) ");
sb.append(super.alterTableDropUniqueConstraint(tableName, uniqueConstraintName)).append(";\n");
sb.append(dropIndex(uniqueConstraintName, tableName));
return sb.toString();
}
/**
* Generate and return the create sequence DDL.
*/
@@ -102,13 +124,24 @@ public class SqlServerDdl extends PlatformDdl {
@Override
public String alterColumnDefaultValue(String tableName, String columnName, String defaultValue) {
// Unfortunately, the SqlServer creates default values with a random name.
// You can specify a name in DDL, but this does not work in conjunction with
// temporal tables in certain cases. So we have to delete the constraint with
// a rather complex statement.
StringBuilder sb = new StringBuilder();
if (DdlHelp.isDropDefault(defaultValue)) {
return "alter table " + tableName + " drop constraint df_" + tableName + "_" + columnName;
sb.append("delimiter $$\n");
sb.append("DECLARE @Tmp nvarchar(200);");
sb.append("select @Tmp = t1.name from sys.default_constraints t1\n");
sb.append(" join sys.columns t2 on t1.object_id = t2.default_object_id\n");
sb.append(" where t1.parent_object_id = OBJECT_ID('").append(tableName)
.append("') and t2.name = '").append(columnName).append("';\n");
sb.append("if @Tmp is not null EXEC('alter table ").append(tableName).append(" drop constraint ' + @Tmp)$$");
} else {
return "alter table " + tableName + " add constraint df_" + tableName + "_" + columnName
+ " default " + defaultValue + " for " + columnName;
sb.append("alter table ").append(tableName);
sb.append(" add default ").append(defaultValue).append(" for ").append(columnName);
}
return sb.toString();
}
@Override
@@ -157,4 +190,28 @@ public class SqlServerDdl extends PlatformDdl {
// do nothing for MS SQL Server (cause it requires stored procedures etc)
}
/**
* It is rather complex to delete a column on SqlServer as there must not exist any references
* (constraints, default values, indices and foreign keys). The list is not yet complete, as
* indices over multiple columns will not yet deleted.
* (This may be changed to delete all refering objects by using the sys.* tables later)
*/
@Override
public void alterTableDropColumn(DdlBuffer buffer, String tableName, String columnName) throws IOException {
buffer.append("-- drop column ").append(tableName).append(".").append(columnName).endOfStatement();
buffer.append(alterTableDropUniqueConstraint(tableName, naming.uniqueConstraintName(tableName, columnName)));
buffer.endOfStatement();
buffer.append(alterColumnDefaultValue(tableName, columnName, DdlHelp.DROP_DEFAULT));
buffer.endOfStatement();
buffer.append(alterTableDropConstraint(tableName, naming.checkConstraintName(tableName, columnName)));
buffer.endOfStatement();
buffer.append(dropIndex(naming.indexName(tableName, columnName), tableName));
buffer.endOfStatement();
buffer.append(alterTableDropForeignKey(tableName, naming.foreignKeyConstraintName(tableName, columnName)));
buffer.endOfStatement();
super.alterTableDropColumn(buffer, tableName, columnName);
}
}
@@ -1,5 +1,6 @@
package io.ebean.dbmigration.ddlgeneration.platform;
import io.ebean.config.DbConstraintNaming;
import io.ebean.config.ServerConfig;
import io.ebean.dbmigration.ddlgeneration.DdlBuffer;
import io.ebean.dbmigration.ddlgeneration.DdlWrite;
@@ -16,11 +17,13 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
private String systemPeriodStart;
private String systemPeriodEnd;
private PlatformDdl platformDdl;
@Override
public void configure(ServerConfig serverConfig, PlatformDdl platformDdl) {
this.systemPeriodStart = serverConfig.getAsOfSysPeriod() + "From";
this.systemPeriodEnd = serverConfig.getAsOfSysPeriod() + "To";
this.platformDdl = platformDdl;
}
@Override
@@ -52,9 +55,19 @@ public class SqlServerHistoryDdl implements PlatformHistoryDdl {
public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
String baseTable = dropHistoryTable.getBaseTable();
DdlBuffer apply = writer.applyHistory();
apply.append("-- dropping history support for ").append(baseTable).endOfStatement();
// drop default constraints
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodStart, DdlHelp.DROP_DEFAULT)).endOfStatement();
apply.append(platformDdl.alterColumnDefaultValue(baseTable, systemPeriodEnd, DdlHelp.DROP_DEFAULT)).endOfStatement();
// switch of versioning & period
apply.append("alter table ").append(baseTable).append(" set (system_versioning = off)").endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement();
// now drop tables & columns
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodStart).endOfStatement();
apply.append("alter table ").append(baseTable).append(" drop column ").append(systemPeriodEnd).endOfStatement();
apply.append("IF OBJECT_ID('").append(baseTable).append("_history', 'U') IS NOT NULL drop table ").append(baseTable).append("_history").endOfStatement();
apply.end();
}
@Override
@@ -9,7 +9,6 @@ import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlValue;
/**
@@ -29,6 +29,10 @@ public class DropTable {
@XmlAttribute(name = "name", required = true)
protected String name;
@XmlAttribute(name = "sequenceCol")
protected String sequenceCol;
@XmlAttribute(name = "sequenceName")
protected String sequenceName;
/**
* Gets the value of the name property.
@@ -50,4 +54,44 @@ public class DropTable {
this.name = value;
}
/**
* Gets the value of the sequenceCol property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceCol() {
return sequenceCol;
}
/**
* Gets the value of the sequenceName property.
*
* @return possible object is
* {@link String }
*/
public String getSequenceName() {
return sequenceName;
}
/**
* Sets the value of the sequenceCol property.
*
* @param value allowed object is
* {@link String }
*/
public void setSequenceCol(String value) {
this.sequenceCol = value;
}
/**
* Sets the value of the sequenceName property.
*
* @param value allowed object is
* {@link String }
*/
public void setSequenceName(String value) {
this.sequenceName = value;
}
}
@@ -186,5 +186,13 @@ public class ObjectFactory {
public Migration createMigration() {
return new Migration();
}
/**
* Create an instance of {@link DdlScript }
*/
public DdlScript createDdlScript() {
return new DdlScript();
}
}
@@ -2,8 +2,6 @@ package io.ebean.dbmigration.model;
import java.util.List;
import javax.sound.midi.MidiDevice.Info;
import io.ebean.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebean.dbmigration.migration.AlterColumn;
import io.ebean.dbmigration.migration.Column;
@@ -1,5 +1,6 @@
package io.ebean.dbmigration.model;
import io.ebean.dbmigration.ddlgeneration.platform.DdlHelp;
import io.ebean.dbmigration.migration.AddColumn;
import io.ebean.dbmigration.migration.AddHistoryTable;
import io.ebean.dbmigration.migration.AddTableComment;
@@ -180,6 +181,24 @@ public class MTable {
public DropTable dropTable() {
DropTable dropTable = new DropTable();
dropTable.setName(name);
// we must add pk col name & sequence name, as we have to delete the sequence also.
if (identityType != IdentityType.GENERATOR && identityType != IdentityType.EXTERNAL) {
String pkCol = null;
for (MColumn column : columns.values()) {
if (column.isPrimaryKey()) {
if (pkCol == null) {
pkCol = column.getName();
} else { // multiple pk cols -> no sequence
pkCol = null;
break;
}
}
}
if (pkCol != null) {
dropTable.setSequenceCol(pkCol);
dropTable.setSequenceName(sequenceName);
}
}
return dropTable;
}
@@ -288,7 +307,11 @@ public class MTable {
if (MColumn.different(comment, newTable.comment)) {
AddTableComment addTableComment = new AddTableComment();
addTableComment.setName(name);
addTableComment.setComment(newTable.comment);
if (newTable.comment == null) {
addTableComment.setComment(DdlHelp.DROP_COMMENT);
} else {
addTableComment.setComment(newTable.comment);
}
modelDiff.addTableComment(addTableComment);
}
}