generateAddColumnAlterTableSQL method

AlterTableSQL generateAddColumnAlterTableSQL(
  1. String table,
  2. String fieldName,
  3. TypeInfo fieldType, {
  4. List<EntityField>? entityFieldAnnotations,
})

Implementation

AlterTableSQL generateAddColumnAlterTableSQL(
  String table,
  String fieldName,
  TypeInfo fieldType, {
  List<EntityField>? entityFieldAnnotations,
}) {
  var q = dialect.elementQuote;
  var columnName = normalizeColumnName(fieldName);
  var fieldSQLType = typeToSQLType(
    fieldType,
    columnName,
    entityFieldAnnotations: entityFieldAnnotations,
  );

  var comment = '${fieldType.toString(withT: false)} $fieldName';

  MapEntry<String, MapEntry<String, String>>? entityType;
  String? refTable;
  String? refColumn;

  if (fieldSQLType == null) {
    entityType = entityTypeToSQLType(
      fieldType,
      columnName,
      entityFieldAnnotations: entityFieldAnnotations,
    );
    if (entityType != null) {
      fieldSQLType = entityType.value.value;

      refTable = entityType.key;
      refColumn = entityType.value.key;

      comment += ' @ $refTable.$refColumn';
    }
  }

  if (fieldSQLType == null) {
    var enumType = enumTypeToSQLType(
      fieldType.type,
      columnName,
      entityFieldAnnotations: entityFieldAnnotations,
    );
    if (enumType != null) {
      var type = enumType.key;
      var values = enumType.value;

      fieldSQLType = _buildEnumSQLType(
        type,
        fieldSQLType,
        values,
        q,
        columnName,
      );

      comment += ' enum(${values.join(', ')})';
    }
  }

  // Without `ALTER TABLE ... ADD CONSTRAINT` (SQLite): the foreign key in
  // the column (`REFERENCES`), and a unique index for `UNIQUE`.
  var alterTableAddConstraint = dialect.alterTableAddConstraint;

  var columnSQL = ' ADD COLUMN $q$columnName$q $fieldSQLType';
  List<SQLColumn>? columnReferences;

  if (entityType != null && !alterTableAddConstraint) {
    var refTableName = entityType.key;
    var refField = entityType.value.key.toLowerCase();
    columnSQL += ' REFERENCES $q$refTableName$q($q$refField$q)';
    columnReferences = [
      SQLColumn(
        table,
        columnName,
        referenceTable: refTableName,
        referenceColumn: refField,
      ),
    ];
  }

  var columnEntry = SQLEntry(
    'ADD',
    columnSQL,
    comment: comment,
    columns: columnReferences,
  );

  List<CreateIndexSQL>? indexes;
  List<AlterTableSQL>? constraints;

  if (entityFieldAnnotations != null && entityFieldAnnotations.isNotEmpty) {
    if (entityFieldAnnotations.hasUnique && !alterTableAddConstraint) {
      var uniqueIndexName = '${table}__${columnName}__unique';
      indexes = [
        CreateIndexSQL.columns(
          dialect,
          table,
          [columnName],
          uniqueIndexName,
          q: q,
          unique: true,
        ),
      ];
    } else if (entityFieldAnnotations.hasUnique) {
      var constrainUniqueName = '${table}__${columnName}__unique';

      var uniqueEntry = SQLEntry(
        'CONSTRAINT',
        ' ADD CONSTRAINT $q$constrainUniqueName$q UNIQUE ($q$columnName$q)',
        columns: [
          SQLColumn(
            table,
            columnName,
            referenceTable: refTable,
            referenceColumn: refColumn,
          ),
        ],
      );

      constraints = [
        AlterTableSQL(dialect, table, [uniqueEntry]),
      ];
    } else if (entityFieldAnnotations.hasIndexed) {
      var indexName = '${table}__${columnName}__idx';
      indexes = [CreateIndexSQL(dialect, table, columnName, indexName)];
    }
  }

  if (entityType != null) {
    var fieldName = columnName;
    var ref = entityType;

    var refTableName = ref.key;
    var refField = ref.value.key.toLowerCase();

    // (Without `ADD CONSTRAINT`: in the column, above.)
    if (alterTableAddConstraint) {
      var constrainName = '${table}__${columnName}__fkey';

      constraints ??= [];

      var constraintEntry = SQLEntry(
        'CONSTRAINT',
        ' ADD CONSTRAINT $q$constrainName$q FOREIGN KEY ($q$columnName$q) REFERENCES $q$refTableName$q($q$refField$q)',
        comment: '$fieldName @ $refTableName.$refField',
        columns: [
          SQLColumn(
            table,
            columnName,
            referenceTable: refTableName,
            referenceColumn: refField,
          ),
        ],
      );

      constraints.add(AlterTableSQL(dialect, table, [constraintEntry]));
    }

    if (!dialect.foreignKeyCreatesImplicitIndex) {
      var indexName = '${table}__${columnName}__idx';

      indexes ??= [];
      indexes.add(
        CreateIndexSQL(dialect, table, columnName, indexName, q: q),
      );
    }
  }

  var alterTableSQL = AlterTableSQL(
    dialect,
    table,
    [columnEntry],
    indexes: indexes,
    constraints: constraints,
  );

  return alterTableSQL;
}