protected void writeForeignKey(
      DdlWrite write,
      String fkName,
      String tableName,
      String[] columns,
      String refTable,
      String[] refColumns,
      String indexName)
      throws IOException {

    tableName = lowerName(tableName);
    DdlBuffer fkeyBuffer = write.applyForeignKeys();
    alterTableAddForeignKey(fkeyBuffer, fkName, tableName, columns, refTable, refColumns);

    if (indexName != null) {
      // no matching unique constraint so add the index
      fkeyBuffer.append(platformDdl.createIndex(indexName, tableName, columns)).endOfStatement();
    }

    fkeyBuffer.end();

    write
        .rollbackForeignKeys()
        .append(platformDdl.alterTableDropForeignKey(tableName, fkName))
        .endOfStatement();

    if (indexName != null) {
      write
          .rollbackForeignKeys()
          .append(platformDdl.dropIndex(indexName, tableName))
          .endOfStatement();
    }

    write.rollbackForeignKeys().end();
  }
  private void writeSequence(DdlWrite writer, CreateTable createTable) throws IOException {

    // explicit sequence use or platform decides
    String explicitSequenceName = createTable.getSequenceName();
    int initial = toInt(createTable.getSequenceInitial());
    int allocate = toInt(createTable.getSequenceAllocate());

    String seqName = namingConvention.sequenceName(createTable.getName(), explicitSequenceName);
    String createSeq = platformDdl.createSequence(seqName, initial, allocate);
    if (createSeq != null) {
      writer.apply().append(createSeq).newLine();
      writer.rollback().append(platformDdl.dropSequence(seqName)).endOfStatement();
    }
  }
  protected void alterColumnType(DdlWrite writer, AlterColumn alter) throws IOException {

    String ddl =
        platformDdl.alterColumnType(alter.getTableName(), alter.getColumnName(), alter.getType());
    if (hasValue(ddl)) {
      writer.apply().append(ddl).endOfStatement();
      if (isTrue(alter.isWithHistory())) {
        // 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();
      }
    }
  }
  protected void addUniqueConstraint(DdlWrite writer, AlterColumn alter, String uqName)
      throws IOException {

    String[] cols = {alter.getColumnName()};

    writer
        .apply()
        .append(platformDdl.alterTableAddUniqueConstraint(alter.getTableName(), uqName, cols))
        .endOfStatement();

    writer
        .rollbackForeignKeys()
        .append(platformDdl.dropIndex(uqName, alter.getTableName()))
        .endOfStatement();
  }
  /**
   * Specific handling of OneToOne unique constraints for MsSqlServer. For all other DB platforms
   * these unique constraints are done inline as per normal.
   */
  protected void writeUniqueOneToOneConstraints(DdlWrite write, CreateTable createTable)
      throws IOException {

    String tableName = createTable.getName();
    for (Column col : externalUnique) {
      String uqName = col.getUniqueOneToOne();
      String[] columnNames = {col.getName()};
      write
          .apply()
          .append(platformDdl.alterTableAddUniqueConstraint(tableName, uqName, columnNames))
          .endOfStatement();

      write.rollbackForeignKeys().append(platformDdl.dropIndex(uqName, tableName)).endOfStatement();
    }
  }
  /** This is mysql specific - alter all the base attributes of the column together. */
  protected void alterColumnBaseAttributes(DdlWrite writer, AlterColumn alter) throws IOException {

    String ddl = platformDdl.alterColumnBaseAttributes(alter);
    if (hasValue(ddl)) {
      writer.apply().append(ddl).endOfStatement();
      if (isTrue(alter.isWithHistory()) && alter.getType() != null) {
        // mysql and sql server column type change allowing nulls in the history table column
        AlterColumn alterHistoryColumn = new AlterColumn();
        alterHistoryColumn.setTableName(historyTable(alter.getTableName()));
        alterHistoryColumn.setColumnName(alter.getColumnName());
        alterHistoryColumn.setType(alter.getType());
        String histColumnDdl = platformDdl.alterColumnBaseAttributes(alterHistoryColumn);
        writer.apply().append(histColumnDdl).endOfStatement();
      }
    }
  }
  @Override
  public void generate(DdlWrite writer, DropIndex dropIndex) throws IOException {

    writer
        .apply()
        .append(platformDdl.dropIndex(dropIndex.getIndexName(), dropIndex.getTableName()))
        .endOfStatement();
  }
  protected void alterColumnDropForeignKey(DdlWrite writer, AlterColumn alter) throws IOException {

    writer
        .apply()
        .append(
            platformDdl.alterTableDropForeignKey(alter.getTableName(), alter.getDropForeignKey()))
        .endOfStatement();
  }
  /**
   * Generate the appropriate 'create table' and matching 'drop table' statements and add them to
   * the 'apply' and 'rollback' buffers.
   */
  @Override
  public void generate(DdlWrite writer, CreateTable createTable) throws IOException {

    reset();

    String tableName = lowerName(createTable.getName());
    List<Column> columns = createTable.getColumn();
    List<Column> pk = determinePrimaryKeyColumns(columns);

    boolean singleColumnPrimaryKey = pk.size() == 1;
    boolean useIdentity = false;
    boolean useSequence = false;

    if (singleColumnPrimaryKey) {
      IdType useDbIdentityType = platformDdl.useIdentityType(createTable.getIdentityType());
      useIdentity = (IdType.IDENTITY == useDbIdentityType);
      useSequence = (IdType.SEQUENCE == useDbIdentityType);
    }

    DdlBuffer apply = writer.apply();
    apply.append("create table ").append(tableName).append(" (");
    for (int i = 0; i < columns.size(); i++) {
      apply.newLine();
      writeColumnDefinition(apply, columns.get(i), useIdentity);
      if (i < columns.size() - 1) {
        apply.append(",");
      }
    }

    writeCheckConstraints(apply, createTable);
    writeUniqueConstraints(apply, createTable);
    writeCompoundUniqueConstraints(apply, createTable);
    if (!pk.isEmpty()) {
      // defined on the columns
      writePrimaryKeyConstraint(apply, tableName, toColumnNames(pk));
    }

    apply.newLine().append(")").endOfStatement();

    // add drop table to the rollback buffer - do this before
    // we drop the related sequence (if sequences are used)
    dropTable(writer.rollback(), tableName);

    if (useSequence) {
      writeSequence(writer, createTable);
    }

    // add blank line for a bit of whitespace between tables
    apply.end();
    writer.rollback().end();

    writeAddForeignKeys(writer, createTable);

    if (isTrue(createTable.isWithHistory())) {
      createWithHistory(writer, createTable.getName());
    }
  }
  protected void alterColumnNotnull(DdlWrite writer, AlterColumn alter) throws IOException {

    String ddl =
        platformDdl.alterColumnNotnull(
            alter.getTableName(), alter.getColumnName(), alter.isNotnull());
    if (hasValue(ddl)) {
      writer.apply().append(ddl).endOfStatement();
    }
  }
  @Override
  public void generate(DdlWrite writer, CreateIndex createIndex) throws IOException {

    String[] cols = toColumnNamesSplit(createIndex.getColumns());
    writer
        .apply()
        .append(
            platformDdl.createIndex(createIndex.getIndexName(), createIndex.getTableName(), cols))
        .endOfStatement();
  }
  protected void alterTableAddForeignKey(
      DdlBuffer buffer,
      String fkName,
      String tableName,
      String[] columns,
      String refTable,
      String[] refColumns)
      throws IOException {

    buffer
        .append(
            platformDdl.alterTableAddForeignKey(tableName, fkName, columns, refTable, refColumns))
        .endOfStatement();
  }
  protected void writeCompoundUniqueConstraints(DdlBuffer apply, CreateTable createTable)
      throws IOException {

    String tableName = createTable.getName();

    List<UniqueConstraint> uniqueConstraints = createTable.getUniqueConstraint();
    for (UniqueConstraint uniqueConstraint : uniqueConstraints) {
      String[] columns = toColumnNamesSplit(uniqueConstraint.getColumnNames());
      apply
          .append(
              platformDdl.alterTableAddUniqueConstraint(
                  tableName, uniqueConstraint.getName(), columns))
          .endOfStatement();
    }
  }
  protected void writeForeignKey(
      DdlWrite write,
      String fkName,
      String tableName,
      String[] columns,
      String refTable,
      String[] refColumns)
      throws IOException {

    tableName = lowerName(tableName);
    DdlBuffer fkeyBuffer = write.applyForeignKeys();
    fkeyBuffer
        .append("alter table ")
        .append(tableName)
        .append(" add constraint ")
        .append(fkName)
        .append(" foreign key");
    appendColumns(columns, fkeyBuffer);
    fkeyBuffer.append(" references ").append(lowerName(refTable));
    appendColumns(refColumns, fkeyBuffer);
    fkeyBuffer.appendWithSpace(platformDdl.getForeignKeyRestrict()).endOfStatement();

    String indexName = determineForeignKeyIndexName(tableName, columns);

    boolean addIndex = indexSet.add(columns);
    if (addIndex) {
      // no matching unique constraint so add the index
      fkeyBuffer.append("create index ").append(indexName).append(" on ").append(tableName);
      appendColumns(columns, fkeyBuffer);
      fkeyBuffer.endOfStatement();
    }

    fkeyBuffer.end();

    if (addIndex) {
      write.rollbackForeignKeys().append("drop index ").append(indexName).endOfStatement();
    }

    write
        .rollbackForeignKeys()
        .append("alter table ")
        .append(tableName)
        .append(" drop constraint ")
        .append(fkName)
        .endOfStatement();

    write.rollbackForeignKeys().end();
  }
  /** Write the unique constraints inline with the create table statement. */
  protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable)
      throws IOException {

    boolean inlineUniqueOneToOne = platformDdl.isInlineUniqueOneToOne();

    List<Column> 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);
      }
    }
  }
 private void writeTableColumns(DdlBuffer apply, List<Column> columns, boolean useIdentity)
     throws IOException {
   platformDdl.writeTableColumns(apply, columns, useIdentity);
 }
 /**
  * Convert the expected logical type into a platform specific one.
  *
  * <p>For example clob -> text for postgres.
  */
 protected String convertToPlatformType(String type, boolean identity) {
   return platformDdl.convert(type, identity);
 }
 /**
  * Convert the table or column name to lower case.
  *
  * <p>This is passed up to the platformDdl to override as desired. Generally lower case with
  * underscore is a good cross database choice for column/table names.
  */
 protected String lowerName(String name) {
   return platformDdl.lowerName(name);
 }
  /** Add 'drop table' statement to the buffer. */
  protected void dropTable(DdlBuffer buffer, String tableName) throws IOException {

    buffer.append(platformDdl.dropTable(tableName)).endOfStatement();
  }
  private void createWithHistory(DdlWrite writer, String name) throws IOException {

    MTable table = writer.getTable(name);
    platformDdl.createWithHistory(writer, table);
  }
 /** Add add history table DDL. */
 @Override
 public void generate(DdlWrite writer, AddHistoryTable addHistoryTable) throws IOException {
   platformDdl.addHistoryTable(writer, addHistoryTable);
 }
 /** Called at the end to generate additional ddl such as regenerate history triggers. */
 @Override
 public void generateExtra(DdlWrite write) throws IOException {
   for (HistoryTableUpdate update : this.regenerateHistoryTriggers.values()) {
     platformDdl.regenerateHistoryTriggers(write, update);
   }
 }
 /** Add drop history table DDL. */
 @Override
 public void generate(DdlWrite writer, DropHistoryTable dropHistoryTable) throws IOException {
   platformDdl.dropHistoryTable(writer, dropHistoryTable);
 }