update method

Future<OrmWriteResult> update(
  1. Map<Column<T, dynamic>, Object?> values, {
  2. List<Column<T, dynamic>> returning = const [],
})

Immediate atomic UPDATE; skip/take/order/rank windows are rejected.

Implementation

Future<OrmWriteResult> update(Map<Column<T, dynamic>, Object?> values,
    {List<Column<T, dynamic>> returning = const []}) {
  try {
    final owner = _directOwner(this);
    _writeType(type);
    if (_offset != 0 ||
        _limit != null ||
        _ordering.isNotEmpty ||
        _rank ||
        _distance) {
      throw const OrmStateException(
          'Direct UPDATE cannot combine with ordering, skip/take, rank or distance');
    }
    if (values.isEmpty) {
      throw const OrmMappingException(
          'UPDATE requires at least one assignment');
    }
    _writeColumns(type, values.keys);
    final selected = _projection(type, returning);
    final parameters = <Object?>[];
    final assignments = <String>[];
    final changed = <Column<T, dynamic>>[];
    for (final assignment in values.entries) {
      final column = assignment.key;
      if (column.keyOrder != null || column.generated || column.version) {
        throw const OrmMappingException(
            'Keys, generated fields and versions are managed by ORM');
      }
      final value = assignment.value;
      String expression;
      if (value is _ArithmeticAssignment<T, dynamic>) {
        if (!['INTEGER', 'REAL'].contains(column.codec.sqlType)) {
          throw const OrmMappingException(
              'Arithmetic assignment requires a numeric target');
        }
        _writeColumns(type, value._columns);
        expression = value._sql(parameters);
      } else {
        if (value is SqlExpression) {
          throw const OrmMappingException(
              'Only constants and add/subtract assignments are supported');
        }
        parameters.add(copyOrmValue(column.encodeValue(value)));
        expression = '?';
      }
      assignments.add('${quoteIdentifier(column.sqlName)}=$expression');
      changed.add(column);
    }
    final version = type.versionColumn;
    if (version != null) {
      assignments.add(
          '${quoteIdentifier(version.sqlName)}=${quoteIdentifier(version.sqlName)}+1');
      changed.add(version);
    }
    final predicate = _predicate?._sql(parameters);
    final sql =
        'UPDATE ${quoteIdentifier(type.tableName)} SET ${assignments.join(',')}${predicate == null ? '' : ' WHERE $predicate'}';
    final bound = List<Object?>.unmodifiable(parameters.map(copyOrmValue));
    return owner._directWrite(() async {
      final broad = (await owner._triggerTables())
              .contains(type.tableName.toLowerCase()) ||
          owner.model.foreignKeys.isNotEmpty;
      final projected = <Column<T, dynamic>>{
        ...changed,
        ...selected,
        if (broad) ...type.keys
      }.toList();
      final mutation = await owner.connection.execute(
          '$sql RETURNING ${projected.map((c) => quoteIdentifier(c.sqlName)).join(',')}',
          bound);
      final rows = await owner._writeRows(type, mutation, projected, selected,
          broad: broad);
      return OrmWriteResult._(mutation.changes, rows);
    });
  } catch (error, stack) {
    return Future.error(error, stack);
  }
}