replace method Null safety

Future<bool> replace(
  1. Insertable<D> entity,
  2. {bool dontExecute = false}
)

Replaces the old version of entity that is stored in the database with the fields of the entity provided here. This implicitly applies a where clause to rows with the same primary key as entity, so that only the row representing outdated data will be replaced.

If entity has absent values (set to null on the DataClass or explicitly to absent on the UpdateCompanion), and a default value for the field exists, that default value will be used. Otherwise, the field will be reset to null. This behavior is different to write, which simply ignores such fields without changing them in the database.

When dontExecute is true (defaults to false), the query will NOT be run, but all the validations are still in place. This is mainly used internally by drift.

Returns true if a row was affected by this operation.

See also:

  • write, which doesn't apply a where statement itself and ignores null values in the entity.
  • InsertStatement.insert with the orReplace parameter, which behaves similar to this method but creates a new row if none exists.

Implementation

Future<bool> replace(Insertable<D> entity, {bool dontExecute = false}) async {
  // We don't turn nulls to absent values here (as opposed to a regular
  // update, where only non-null fields will be written).
  final columns = entity.toColumns(false);
  _sourceTable
      .validateIntegrity(entity, isInserting: true)
      .throwIfInvalid(entity);
  assert(
      whereExpr == null,
      'When using replace on an update statement, you may not use where(...)'
      'as well. The where clause will be determined automatically');

  whereSamePrimaryKey(entity);

  // copying to work around type issues - Map<String, Variable> extends
  // Map<String, Expression> but crashes when adding anything that is not
  // a Variable.
  _updatedFields = columns is Map<String, Variable>
      ? Map<String, Expression>.of(columns)
      : columns;

  final primaryKeys = _sourceTable.$primaryKey.map((c) => c.$name);

  // entityToSql doesn't include absent values, so we might have to apply the
  // default value here
  for (final column in table.$columns) {
    // if a default value exists and no value is set, apply the default
    if (column.defaultValue != null &&
        !_updatedFields.containsKey(column.$name)) {
      _updatedFields[column.$name] = column.defaultValue!;
    }
  }

  // Don't update the primary key
  _updatedFields.removeWhere((key, _) => primaryKeys.contains(key));

  if (dontExecute) return false;
  final updatedRows = await _performQuery();
  return updatedRows != 0;
}