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;
}