001package io.ebeaninternal.dbmigration.ddlgeneration.platform; 002 003import io.ebean.config.DatabaseConfig; 004import io.ebean.config.DbConstraintNaming; 005import io.ebeaninternal.dbmigration.ddlgeneration.DdlAlterTable; 006import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer; 007import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite; 008import io.ebeaninternal.dbmigration.migration.AddHistoryTable; 009import io.ebeaninternal.dbmigration.migration.AlterColumn; 010import io.ebeaninternal.dbmigration.migration.DropHistoryTable; 011import io.ebeaninternal.dbmigration.model.MTable; 012 013/** 014 * @author Vilmos Nagy 015 */ 016public class SqlServerHistoryDdl implements PlatformHistoryDdl { 017 018 private String systemPeriodStart; 019 private String systemPeriodEnd; 020 private PlatformDdl platformDdl; 021 protected DbConstraintNaming constraintNaming; 022 protected String historySuffix; 023 024 @Override 025 public void configure(DatabaseConfig config, PlatformDdl platformDdl) { 026 this.systemPeriodStart = config.getAsOfSysPeriod() + "From"; 027 this.systemPeriodEnd = config.getAsOfSysPeriod() + "To"; 028 this.platformDdl = platformDdl; 029 030 this.constraintNaming = config.getConstraintNaming(); 031 this.historySuffix = config.getHistoryTableSuffix(); 032 } 033 034 @Override 035 public void createWithHistory(DdlWrite writer, MTable table) { 036 String baseTable = table.getName(); 037 enableSystemVersioning(writer, baseTable); 038 } 039 040 private void enableSystemVersioning(DdlWrite writer, String baseTable) { 041 DdlBuffer apply = writer.applyPostAlter(); 042 apply.append("alter table ").append(quote(baseTable)).newLine() 043 .append(" add ").append(systemPeriodStart).append(" datetime2 GENERATED ALWAYS AS ROW START NOT NULL DEFAULT SYSUTCDATETIME(),").newLine() 044 .append(" ").append(systemPeriodEnd).append(" datetime2 GENERATED ALWAYS AS ROW END NOT NULL DEFAULT '9999-12-31T23:59:59.9999999',").newLine() 045 .append("period for system_time (").append(systemPeriodStart).append(", ").append(systemPeriodEnd).append(")").endOfStatement(); 046 047 apply.append("alter table ").append(baseTable).append(" set (system_versioning = on (history_table=") 048 .append(historyTableWithSchema(baseTable)).append("))").endOfStatement(); 049 050 DdlBuffer drop = writer.dropAll(); 051 drop.append("IF OBJECT_ID('").append(quote(baseTable)).append("', 'U') IS NOT NULL alter table ") 052 .append(quote(baseTable)).append(" set (system_versioning = off)").endOfStatement(); 053 drop.append("IF OBJECT_ID('").append(historyTableName(baseTable)).append("', 'U') IS NOT NULL drop table ") 054 .append(historyTableName(baseTable)).endOfStatement(); 055 } 056 057 @Override 058 public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) { 059 String baseTable = dropHistoryTable.getBaseTable(); 060 // drop default constraints 061 AlterColumn alter = new AlterColumn(); 062 alter.setTableName(baseTable); 063 alter.setDefaultValue(DdlHelp.DROP_DEFAULT); 064 alter.setColumnName(systemPeriodStart); 065 platformDdl.alterColumn(writer, alter); 066 alter.setColumnName(systemPeriodEnd); 067 platformDdl.alterColumn(writer, alter); 068 069 // switch of versioning & period - must be done before altering 070 DdlBuffer apply = writer.apply(); 071 apply.append("-- dropping history support for ").append(baseTable).endOfStatement(); 072 apply.append("alter table ").append(quote(baseTable)).append(" set (system_versioning = off)").endOfStatement(); 073 apply.append("alter table ").append(quote(baseTable)).append(" drop period for system_time").endOfStatement(); 074 apply.end(); 075 // now drop tables & columns, they will go to alter table/post alter buffers 076 platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodStart); 077 platformDdl.alterTableDropColumn(writer, baseTable, systemPeriodEnd); 078 writer.applyPostAlter().appendStatement(platformDdl.dropTable(historyTableName(baseTable))); 079 } 080 081 @Override 082 public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) { 083 String baseTable = addHistoryTable.getBaseTable(); 084 enableSystemVersioning(writer, baseTable); 085 } 086 087 @Override 088 public void updateTriggers(DdlWrite writer, String tableName) { 089 DdlAlterTable alter = platformDdl.alterTable(writer, tableName); 090 writer.getTable(tableName); 091 if (!alter.isHistoryHandled()) { 092 // SQL Server 2016 does not need triggers 093 DdlBuffer apply = writer.apply(); 094 apply.append("-- alter table ").append(quote(tableName)).append(" set (system_versioning = off (history_table=") 095 .append(historyTableWithSchema(tableName)).append("))").endOfStatement(); 096 apply.append("-- history migration goes here").newLine(); 097 apply.append("-- alter table ").append(quote(tableName)).append(" set (system_versioning = on (history_table=") 098 .append(historyTableWithSchema(tableName)).append("))").endOfStatement(); 099 } 100 alter.setHistoryHandled(); 101 } 102 103 protected String quote(String baseTable) { 104 return platformDdl.quote(baseTable); 105 } 106 107 protected String normalise(String tableName) { 108 return constraintNaming.normaliseTable(tableName); 109 } 110 111 protected String historyTableName(String baseTableName) { 112 return normalise(baseTableName) + historySuffix; 113 } 114 115 protected String historyTableWithSchema(String baseTableName) { 116 String historyTable = historyTableName(baseTableName); 117 int lastPeriod = baseTableName.lastIndexOf('.'); 118 if (lastPeriod == -1) { 119 // history must contain schema, add the default schema if none was specified 120 return "dbo." + historyTable; 121 } else { 122 return baseTableName.substring(0, lastPeriod + 1) + historyTable; 123 } 124 } 125 126}