MssqlTableBinding<TRow> constructor

MssqlTableBinding<TRow>({
  1. required String schema,
  2. required String table,
  3. required List<MssqlBoundColumn> columns,
  4. required TRow fromRow(
    1. MssqlRow row
    ),
  5. required Map<String, Object?> toColumns(
    1. TRow row
    ),
  6. required Object? readColumn(
    1. TRow row,
    2. String column
    ),
  7. TRow applyIdentity(
    1. TRow row,
    2. Object? identity
    )?,
  8. TRow applyRelations(
    1. TRow row,
    2. Map<String, Object?> relations
    )?,
  9. bool isRelationLoaded(
    1. TRow row,
    2. String name
    )?,
  10. List<String> primaryKey = const <String>[],
  11. String? identityColumn,
  12. MssqlInsertStrategy insertStrategy = MssqlInsertStrategy.noKeyReadback,
  13. String schemaFingerprint = '',
  14. MssqlDecimalMode decimalMode = MssqlDecimalMode.exact,
  15. int apiVersion = MssqlApiVersion.current,
  16. MssqlSoftDelete? softDelete,
  17. MssqlTimestamps timestamps = const MssqlTimestamps(),
  18. List<MssqlScope> scopes = const <MssqlScope>[],
})

Implementation

MssqlTableBinding({
  required this.schema,
  required this.table,
  required List<MssqlBoundColumn> columns,
  required this.fromRow,
  required this.toColumns,
  required this.readColumn,
  this.applyIdentity,
  this.applyRelations,
  this.isRelationLoaded,
  List<String> primaryKey = const <String>[],
  this.identityColumn,
  this.insertStrategy = MssqlInsertStrategy.noKeyReadback,
  this.schemaFingerprint = '',
  this.decimalMode = MssqlDecimalMode.exact,
  this.apiVersion = MssqlApiVersion.current,
  MssqlSoftDelete? softDelete,
  MssqlTimestamps timestamps = const MssqlTimestamps(),
  List<MssqlScope> scopes = const <MssqlScope>[],
}) : columns = List<MssqlBoundColumn>.unmodifiable(columns),
     primaryKey = List<String>.unmodifiable(primaryKey),
     scopes = List<MssqlScope>.unmodifiable(scopes),
     // Convention columns are resolved to the schema's own spelling here,
     // once, rather than at each place that reads them. Lookup is
     // case-insensitive, so a configuration saying `deletedat` validates
     // against `DeletedAt` — and then every predicate and assignment built
     // from it has to say `DeletedAt` too, or the SQL breaks on a database
     // with a case-sensitive collation.
     softDelete = _canonicalSoftDelete(softDelete, columns),
     timestamps = _canonicalTimestamps(timestamps, columns) {
  for (final key in this.primaryKey) {
    if (column(key) == null) {
      throw ArgumentError.value(
        key,
        'primaryKey',
        'Names a column $schema.$table does not have.',
      );
    }
  }
  // Validation reads the fields, not the parameters: those are the
  // canonicalized values, and they are what every message should name.
  final soft = this.softDelete;
  _validateConventionColumn(soft?.column, 'softDelete');
  if (soft != null) {
    final softColumn = column(soft.column)!;
    if (soft.deletedValue == null && !_storesServerClock(softColumn)) {
      throw ArgumentError.value(
        soft.column,
        'softDelete',
        'A timestamp soft-delete column must use a date/time SQL type.',
      );
    }
    if (soft.aliveValue == null && !softColumn.nullable) {
      throw ArgumentError.value(
        soft.column,
        'softDelete',
        'A null-means-live soft-delete column must be nullable.',
      );
    }
    if (soft.aliveValue != null && soft.deletedValue == null) {
      throw ArgumentError.value(
        soft.column,
        'softDelete',
        'A value-based soft-delete convention needs deletedValue.',
      );
    }
  }
  final stamps = this.timestamps;
  _validateConventionColumn(stamps.createdColumn, 'timestamps.createdColumn');
  _validateConventionColumn(stamps.updatedColumn, 'timestamps.updatedColumn');
  for (final name in <String?>[stamps.createdColumn, stamps.updatedColumn]) {
    if (name != null && !_storesServerClock(column(name)!)) {
      throw ArgumentError.value(
        name,
        'timestamps',
        'Timestamp columns must use a date/time SQL type.',
      );
    }
  }
  if (stamps.createdColumn != null &&
      stamps.createdColumn!.toLowerCase() ==
          stamps.updatedColumn?.toLowerCase()) {
    throw ArgumentError.value(
      stamps.createdColumn,
      'timestamps',
      'Created and updated timestamps must use different columns.',
    );
  }
  if (!MssqlApiVersion.supports(apiVersion)) {
    throw ArgumentError(
      MssqlApiVersion.mismatchMessage(apiVersion, '$schema.$table'),
    );
  }
  final softName = softDelete?.column.toLowerCase();
  if (softName != null &&
      (softName == timestamps.createdColumn?.toLowerCase() ||
          softName == timestamps.updatedColumn?.toLowerCase())) {
    throw ArgumentError.value(
      softDelete!.column,
      'softDelete',
      'A soft-delete column cannot also be a timestamp column.',
    );
  }
  final scopeNames = <String>{};
  for (final scope in this.scopes) {
    final name = scope.name.trim();
    if (name.isEmpty) {
      throw ArgumentError.value(
        scope.name,
        'scopes',
        'A scope needs a name.',
      );
    }
    if (!scopeNames.add(name)) {
      throw ArgumentError.value(
        scope.name,
        'scopes',
        'Scope names must be unique within $schema.$table.',
      );
    }
  }
}