001package io.ebeaninternal.dbmigration.ddlgeneration.platform;
002
003import io.ebean.config.DatabaseConfig;
004import io.ebeaninternal.dbmigration.ddlgeneration.DdlBuffer;
005import io.ebeaninternal.dbmigration.ddlgeneration.DdlWrite;
006import io.ebeaninternal.dbmigration.migration.AddHistoryTable;
007import io.ebeaninternal.dbmigration.migration.DropHistoryTable;
008import io.ebeaninternal.dbmigration.model.MColumn;
009import io.ebeaninternal.dbmigration.model.MTable;
010
011import java.io.IOException;
012import java.util.Collection;
013import java.util.Map;
014import java.util.concurrent.ConcurrentHashMap;
015import java.util.concurrent.atomic.AtomicInteger;
016
017public class HanaHistoryDdl implements PlatformHistoryDdl {
018
019  private String systemPeriodStart;
020  private String systemPeriodEnd;
021  private PlatformDdl platformDdl;
022  private String historySuffix;
023  private final AtomicInteger counter = new AtomicInteger(0);
024  private Map<String, String> createdHistoryTables = new ConcurrentHashMap<>();
025
026  @Override
027  public void configure(DatabaseConfig config, PlatformDdl platformDdl) {
028    this.systemPeriodStart = config.getAsOfSysPeriod() + "_start";
029    this.systemPeriodEnd = config.getAsOfSysPeriod() + "_end";
030    this.platformDdl = platformDdl;
031    this.historySuffix = config.getHistoryTableSuffix();
032  }
033
034  @Override
035  public void createWithHistory(DdlWrite writer, MTable table) throws IOException {
036    String tableName = table.getName();
037    String historyTableName = tableName + historySuffix;
038    DdlBuffer apply = writer.applyHistoryView();
039    if (apply.isEmpty()) {
040      createdHistoryTables.clear();
041    }
042
043    apply.append(platformDdl.getCreateTableCommandPrefix()).append(" ").append(historyTableName).append(" (").newLine();
044
045    // create history table
046    Collection<MColumn> cols = table.allColumns();
047    for (MColumn column : cols) {
048      if (!column.isDraftOnly()) {
049        writeColumnDefinition(apply, column.getName(), column.getType(), column.getDefaultValue(), column.isNotnull(),
050          column.isIdentity() ? platformDdl.identitySuffix : null);
051        apply.append(",").newLine();
052      }
053    }
054    writeColumnDefinition(apply, systemPeriodStart, "TIMESTAMP", null, false, null);
055    apply.append(",").newLine();
056    writeColumnDefinition(apply, systemPeriodEnd, "TIMESTAMP", null, false, null);
057    apply.newLine().append(")").endOfStatement();
058
059    // enable system versioning
060    apply.append("alter table ").append(tableName).append(" add (").newLine();
061    apply.append("    ").append(systemPeriodStart).append(" TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW START, ").newLine();
062    apply.append("    ").append(systemPeriodEnd).append(" TIMESTAMP NOT NULL GENERATED ALWAYS AS ROW END").newLine();
063    apply.append(")").endOfStatement();
064
065    apply.append("alter table ").append(tableName).append(" add period for system_time(").append(systemPeriodStart)
066      .append(",").append(systemPeriodEnd).append(")").endOfStatement();
067
068    enableSystemVersioning(apply, tableName, historyTableName, true, false);
069
070    createdHistoryTables.put(tableName, historyTableName);
071
072    dropHistoryTable(writer.dropAll(), tableName, historyTableName);
073  }
074
075  @Override
076  public void dropHistoryTable(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
077    dropHistoryTable(writer.applyDropDependencies(), dropHistoryTable.getBaseTable(),
078      dropHistoryTable.getBaseTable() + historySuffix);
079  }
080
081  protected void dropHistoryTable(DdlBuffer apply, String baseTable, String historyTable) throws IOException {
082    // disable system versioning
083    disableSystemVersioning(apply, baseTable);
084
085    apply.append("alter table ").append(baseTable).append(" drop period for system_time").endOfStatement();
086
087    // drop the period columns
088    apply.append("alter table ").append(baseTable).append(" drop (").append(systemPeriodStart).append(",")
089      .append(systemPeriodEnd).append(")").endOfStatement();
090
091    // drop the history table
092    apply.append("drop table ").append(historyTable).append(" cascade").endOfStatement();
093  }
094
095  @Override
096  public void addHistoryTable(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
097    MTable table = writer.getTable(addHistoryTable.getBaseTable());
098    if (table == null) {
099      throw new IllegalStateException("MTable " + addHistoryTable.getBaseTable() + " not found in writer? (required for history DDL)");
100    }
101    createWithHistory(writer, table);
102  }
103
104  @Override
105  public void updateTriggers(DdlWrite write, HistoryTableUpdate baseTable) {
106    // nothing to do
107  }
108
109  protected void writeColumnDefinition(DdlBuffer buffer, String columnName, String type, String defaultValue,
110                                       boolean isNotNull, String generated) throws IOException {
111
112    String platformType = platformDdl.convert(type);
113    buffer.append(" ").append(platformDdl.lowerColumnName(columnName));
114    buffer.append(" ").append(platformType);
115    if (defaultValue != null) {
116      buffer.append(" default ").append(defaultValue);
117    }
118    if (isNotNull) {
119      buffer.append(" not null");
120    }
121    if (generated != null) {
122      buffer.append(" ").append(generated);
123    }
124  }
125
126  public void disableSystemVersioning(DdlBuffer apply, String tableName) throws IOException {
127    disableSystemVersioning(apply, tableName, false);
128  }
129
130  public void disableSystemVersioning(DdlBuffer apply, String tableName, boolean uniqueStatement) throws IOException {
131    apply.append("alter table ").append(tableName).append(" drop system versioning");
132    if (uniqueStatement) {
133      // needed for the DB migration test to prevent the statement from being filtered
134      // out as a duplicate
135      apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */");
136    }
137    apply.endOfStatement();
138  }
139
140  public void enableSystemVersioning(DdlBuffer apply, String tableName, String historyTableName, boolean validated,
141                                     boolean uniqueStatement) throws IOException {
142    apply.append("alter table ").append(tableName).append(" add system versioning history table ").append(historyTableName);
143    if (!validated) {
144      apply.append(" not validated");
145    }
146    if (uniqueStatement) {
147      // needed for the DB migration test to prevent the statement from being filtered
148      // out as a duplicate
149      apply.append(" /* ").append(String.valueOf(counter.getAndIncrement())).append(" */");
150    }
151    apply.endOfStatement();
152  }
153
154  public boolean isSystemVersioningEnabled(String tableName) {
155    return !createdHistoryTables.containsKey(tableName);
156  }
157}