/**
   * 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());
    }
  }
  /** Write the unique constraints inline with the create table statement. */
  protected void writeUniqueConstraints(DdlBuffer apply, CreateTable createTable)
      throws IOException {

    List<Column> columns = createTable.getColumn();
    for (Column column : columns) {
      if (isTrue(column.isUnique())) {
        inlineUniqueConstraintSingle(apply, createTable.getName(), column);
        indexSet.add(column);
      }
    }
  }
  /** Write all the check constraints. */
  protected void writeCheckConstraints(DdlBuffer apply, CreateTable createTable)
      throws IOException {

    List<Column> columns = createTable.getColumn();
    for (Column column : columns) {
      String checkConstraint = column.getCheckConstraint();
      if (hasValue(checkConstraint)) {
        writeCheckConstraint(apply, createTable.getName(), column, checkConstraint);
      }
    }
  }
  protected void writeAddForeignKeys(DdlWrite write, CreateTable createTable) throws IOException {

    String tableName = createTable.getName();
    List<Column> columns = createTable.getColumn();
    for (Column column : columns) {
      String references = column.getReferences();
      if (hasValue(references)) {
        writeForeignKey(write, tableName, column.getName(), references);
      }
    }

    writeAddCompoundForeignKeys(write, createTable);
  }
  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 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 writeAddCompoundForeignKeys(DdlWrite write, CreateTable createTable)
      throws IOException {

    String tableName = createTable.getName();

    List<ForeignKey> foreignKey = createTable.getForeignKey();
    for (ForeignKey key : foreignKey) {

      String refTableName = key.getRefTableName();
      String fkName = determineForeignKeyConstraintName(tableName, refTableName);
      String[] cols = toColumnNamesSplit(key.getColumnNames());
      String[] refColumns = toColumnNamesSplit(key.getRefColumnNames());

      writeForeignKey(write, fkName, tableName, cols, refTableName, refColumns);
    }
  }
  /**
   * 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();
    }
  }
  /** 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);
      }
    }
  }