generateCreateTableSQL method

CreateTableSQL generateCreateTableSQL({
  1. EntityRepository<Object>? entityRepository,
  2. Object? obj,
  3. Type? type,
  4. String? name,
  5. String? tableName,
  6. bool ifNotExists = true,
  7. bool sortColumns = true,
})

Implementation

CreateTableSQL generateCreateTableSQL(
    {EntityRepository? entityRepository,
    Object? obj,
    Type? type,
    String? name,
    String? tableName,
    bool ifNotExists = true,
    bool sortColumns = true}) {
  entityRepository ??=
      getEntityRepository(type: type, obj: obj, name: name, tableName: name);
  if (entityRepository == null) {
    throw ArgumentError(
        "Can't resolve `EntityRepository`> obj: $obj ; type: $type ; name: $name ; tableName: $tableName");
  }

  var q = dialect.elementQuote;

  var entityType = entityRepository.type;
  var entityHandler = entityRepository.entityHandler;

  var table = getTableForEntityRepository(entityRepository);

  var idFieldName = entityHandler.idFieldName();
  var idType = entityHandler.idType();

  var idColumnName = normalizeColumnName(idFieldName);
  var idAnnotations = entityHandler
      .getFieldEntityAnnotations(null, idFieldName)
      ?.whereType<EntityField>()
      .toList();

  var idTypeSQL =
      primaryKeyTypeToSQLType(idType, entityFieldAnnotations: idAnnotations);

  var sqlEntries = <SQLEntry>[
    SQLEntry('COLUMN', ' $q$idColumnName$q $idTypeSQL',
        comment: '$idType $idFieldName',
        columns: [SQLColumn(table, idColumnName)]),
  ];

  var indexSQLs = <CreateIndexSQL>[];

  var fieldsEntries = entityHandler
      .fieldsTypes()
      .entries
      .where((e) => !e.value.isListEntityOrReference)
      .toList();

  if (sortColumns) {
    fieldsEntries.sort((a, b) => a.key.compareTo(b.key));
  }

  var referenceFields =
      <String, MapEntry<String, MapEntry<String, String>>>{};

  for (var e in fieldsEntries) {
    var fieldName = e.key;
    var fieldType = e.value;
    if (fieldName == idFieldName) continue;

    var entityFieldAnnotations = entityHandler
        .getFieldEntityAnnotations(null, fieldName)
        ?.whereType<EntityField>()
        .toList();

    if (entityFieldAnnotations != null && entityFieldAnnotations.hasHidden) {
      continue;
    }

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

    String? refTable;
    String? refColumn;

    var fieldSQLType = typeToSQLType(fieldType, columnName,
        entityFieldAnnotations: entityFieldAnnotations);

    if (fieldSQLType == null) {
      var entityType = entityTypeToSQLType(fieldType, columnName,
          entityFieldAnnotations: entityFieldAnnotations);
      if (entityType != null) {
        var fieldEntityType = fieldType.entityType;
        if (fieldEntityType != null &&
            isSiblingEntityType(entityRepository, fieldEntityType)) {
          referenceFields[columnName] = entityType;
        }

        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(', ')})';
      }
    }

    if (fieldSQLType == null) {
      _log.warning("Can't define field SQL type: `$table`.`$fieldName`");
      continue;
    }

    sqlEntries.add(SQLEntry('COLUMN', ' $q$columnName$q $fieldSQLType',
        comment: comment,
        columns: [
          SQLColumn(table, columnName,
              referenceTable: refTable, referenceColumn: refColumn)
        ]));

    if (entityFieldAnnotations != null) {
      if (entityFieldAnnotations.hasUnique) {
        var constrainUniqueName = '${table}__${columnName}__unique';

        sqlEntries.add(SQLEntry('CONSTRAINT',
            ' CONSTRAINT $q$constrainUniqueName$q UNIQUE ($q$columnName$q)',
            columns: [
              SQLColumn(table, columnName,
                  referenceTable: refTable, referenceColumn: refColumn)
            ]));
      } else if (entityFieldAnnotations.hasIndexed) {
        var indexName = '${table}__${columnName}__idx';
        indexSQLs
            .add(CreateIndexSQL(dialect, table, columnName, indexName, q: q));
      }
    }
  }

  for (var e in referenceFields.entries) {
    var fieldName = e.key;
    var ref = e.value;

    var columnName = normalizeColumnName(fieldName);

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

    var constrainName = '${table}__${columnName}__fkey';

    sqlEntries.add(SQLEntry('CONSTRAINT',
        ' 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)
        ]));
  }

  sqlEntries.sort((a, b) {
    var c1 = a.type == 'CONSTRAINT';
    var c2 = b.type == 'CONSTRAINT';

    if (c1) {
      return c2 ? 0 : 1;
    } else {
      return c2 ? -1 : 0;
    }
  });

  var createSQL = CreateTableSQL(dialect, table, sqlEntries,
      q: q, entityRepository: entityRepository, indexes: indexSQLs);

  var relationshipSQLs = <CreateTableSQL>[];

  var relationshipEntries = entityHandler
      .fieldsTypes()
      .entries
      .where((e) => e.value.isListEntityOrReference)
      .toList();

  for (var e in relationshipEntries) {
    var fieldName = e.key;
    var fieldType = e.value.arguments0!;

    var entityFieldAnnotations = entityHandler
        .getFieldEntityAnnotations(null, fieldName)
        ?.whereType<EntityField>()
        .toList();

    var columnName = normalizeColumnName(fieldName);

    var srcFieldName = normalizeColumnName(table);
    var relSrcType = foreignKeyTypeToSQLType(
        TypeInfo.fromType(idType), srcFieldName,
        entityFieldAnnotations: entityFieldAnnotations);

    var relDstType = entityTypeToSQLType(fieldType, null);
    if (relDstType == null) continue;

    var relDstTable = relDstType.key;
    var relDstTableId = relDstType.value.key;
    var relDstTypeSQL = relDstType.value.value;

    var dstFieldName = normalizeColumnName(relDstTable);

    var relName = '${table}__${columnName}__rel';

    var constrainUniqueName = '${relName}__unique';
    var constrainSrcName = '${relName}__${srcFieldName}__fkey';
    var constrainDstName = '${relName}__${dstFieldName}__fkey';

    var sqlRelEntries = <SQLEntry>[
      SQLEntry('COLUMN', ' $q$srcFieldName$q $relSrcType NOT NULL',
          comment: '$entityType.${e.key}',
          columns: [
            SQLColumn(relName, srcFieldName,
                referenceTable: table, referenceColumn: idColumnName)
          ]),
      SQLEntry('COLUMN', ' $q$dstFieldName$q $relDstTypeSQL NOT NULL',
          comment:
              '${e.value.toString(withT: false)} @ $relDstTable.$relDstTableId',
          columns: [
            SQLColumn(relName, dstFieldName,
                referenceTable: relDstTable, referenceColumn: relDstTableId)
          ]),
      SQLEntry('CONSTRAINT',
          ' CONSTRAINT $q$constrainUniqueName$q UNIQUE ($q$srcFieldName$q, $q$dstFieldName$q)',
          columns: [
            SQLColumn(relName, srcFieldName),
            SQLColumn(relName, dstFieldName)
          ]),
      SQLEntry('CONSTRAINT',
          ' CONSTRAINT $q$constrainSrcName$q FOREIGN KEY ($q$srcFieldName$q) REFERENCES $q$table$q($q$idColumnName$q) ON DELETE CASCADE',
          comment: ' $srcFieldName @ $table.$idColumnName',
          columns: [
            SQLColumn(relName, srcFieldName,
                referenceTable: table, referenceColumn: idColumnName)
          ]),
      SQLEntry('CONSTRAINT',
          ' CONSTRAINT $q$constrainDstName$q FOREIGN KEY ($q$dstFieldName$q) REFERENCES $q$relDstTable$q($q$relDstTableId$q) ON DELETE CASCADE',
          comment: ' $dstFieldName @ $relDstTable.$relDstTableId',
          columns: [
            SQLColumn(relName, dstFieldName,
                referenceTable: relDstTable, referenceColumn: relDstTableId)
          ])
    ];

    var relSQL = CreateTableSQL(dialect, relName, sqlRelEntries,
        q: q, parentTable: table);
    relationshipSQLs.add(relSQL);
  }

  createSQL.relationships = relationshipSQLs;

  return createSQL;
}