/** Return the list of columns that make the primary key. */
 protected List<Column> determinePrimaryKeyColumns(List<Column> columns) {
   List<Column> pk = new ArrayList<Column>(3);
   for (Column column : columns) {
     if (isTrue(column.isPrimaryKey())) {
       pk.add(column);
     }
   }
   return pk;
 }
  /** 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 the unique constraint inline with the create table statement. */
  protected void inlineUniqueConstraintSingle(DdlBuffer buffer, String tableName, Column column)
      throws IOException {

    String uqName = determineUniqueConstraintName(tableName, column.getName());

    buffer.append(",").newLine();
    buffer.append("  constraint ").append(uqName).append(" unique ");
    buffer.append("(");
    buffer.append(lowerName(column.getName()));
    buffer.append(")");
  }
  /** 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);
  }
  /** Write the unique constraint inline with the create table statement. */
  protected void inlineUniqueConstraintSingle(DdlBuffer buffer, Column column) throws IOException {

    String uqName = column.getUnique();
    if (uqName == null) {
      uqName = column.getUniqueOneToOne();
    }

    buffer.append(",").newLine();
    buffer.append("  constraint ").append(uqName).append(" unique ");
    buffer.append("(");
    buffer.append(lowerName(column.getName()));
    buffer.append(")");
  }
  /**
   * 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 column definition to the create table statement. */
  protected void writeColumnDefinition(DdlBuffer buffer, Column column, boolean useIdentity)
      throws IOException {

    boolean identityColumn = useIdentity && isTrue(column.isPrimaryKey());
    String platformType = convertToPlatformType(column.getType(), identityColumn);

    buffer.append("  ");
    buffer.append(lowerName(column.getName()), 30);
    buffer.append(platformType);
    if (isTrue(column.isNotnull()) || isTrue(column.isPrimaryKey())) {
      buffer.append(" not null");
    }

    // add check constraints later as we really want to give them a nice name
    // so that the database can potentially provide a nice SQL error
  }
  /** 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);
      }
    }
  }
  /** Write a check constraint. */
  protected void writeCheckConstraint(DdlBuffer buffer, Column column, String checkConstraint)
      throws IOException {

    String ckName = column.getCheckConstraintName();

    buffer.append(",").newLine();
    buffer.append("  constraint ").append(ckName);
    buffer.append(" ").append(checkConstraint);
  }
  protected void writeForeignKey(DdlWrite write, String tableName, Column column)
      throws IOException {

    String fkName = column.getForeignKeyName();
    String references = column.getReferences();
    int pos = references.lastIndexOf('.');
    if (pos == -1) {
      throw new IllegalStateException(
          "Expecting period '.' character for table.column split but not found in ["
              + references
              + "]");
    }
    String refTableName = references.substring(0, pos);
    String refColumnName = references.substring(pos + 1);

    String[] cols = {column.getName()};
    String[] refCols = {refColumnName};

    writeForeignKey(
        write, fkName, tableName, cols, refTableName, refCols, column.getForeignKeyIndex());
  }
  protected void alterTableAddColumn(
      DdlBuffer buffer, String tableName, Column column, boolean onHistoryTable)
      throws IOException {

    buffer
        .append("alter table ")
        .append(tableName)
        .append(" add column ")
        .append(column.getName())
        .append(" ")
        .append(column.getType());

    if (!onHistoryTable) {
      if (isTrue(column.isNotnull())) {
        buffer.append(" not null");
      }
      if (hasValue(column.getCheckConstraint())) {
        buffer.append(" ").append(column.getCheckConstraint());
      }
    }
    buffer.endOfStatement();
  }
  /** Add add column DDL. */
  @Override
  public void generate(DdlWrite writer, AddColumn addColumn) throws IOException {

    String tableName = addColumn.getTableName();
    List<Column> columns = addColumn.getColumn();
    for (Column column : columns) {
      alterTableAddColumn(writer.apply(), tableName, column, false);
      alterTableDropColumn(writer.rollback(), tableName, column.getName());
    }

    if (isTrue(addColumn.isWithHistory())) {
      // make same changes to the history table
      String historyTable = historyTable(tableName);
      for (Column column : columns) {
        regenerateHistoryTriggers(tableName, HistoryTableUpdate.Change.ADD, column.getName());
        alterTableAddColumn(writer.apply(), historyTable, column, true);
        alterTableDropColumn(writer.rollback(), historyTable, column.getName());
      }
    }

    // add a bit of whitespace
    writer.apply().end();
    writer.rollback().end();
  }
 /** Construct representing as a single column index. */
 public IndexColumns(Column column) {
   columns.add(column.getName());
 }