getOrCreate method

Future<TRow> getOrCreate({
  1. required MssqlUniqueMatch key,
  2. required MssqlWriteAssignments create,
  3. bool restoreExisting = false,
  4. bool includeDeleted = false,
})

Inserts create, or returns the row already owning key.

Only conflicts from MssqlUniqueMatch.indexName are recovered. Deleted matches follow restoreExisting and includeDeleted.

Implementation

Future<TRow> getOrCreate({
  required MssqlUniqueMatch key,
  required MssqlWriteAssignments create,
  bool restoreExisting = false,
  bool includeDeleted = false,
}) async {
  _checkDecimalAgreement();
  var values = create;
  for (final entry in key.values.entries) {
    final column = binding.column(entry.key);
    if (column == null) {
      throw ArgumentError.value(
        entry.key,
        'key',
        '${binding.qualifiedName} has no column "${entry.key}".',
      );
    }
    final bound = MssqlBoundValue(column.bind(entry.value));
    final existing = values[entry.key];
    if (existing is MssqlBoundValue &&
        existing.value.value != bound.value.value) {
      throw ArgumentError.value(
        entry.value,
        'create',
        'getOrCreate unique "${key.indexName}" is ${entry.key}='
            '${entry.value}, but create sets it to ${existing.value.value}.',
      );
    }
    values = values.withValue(column.name, bound);
  }
  try {
    return await _insertAssignments(values, key);
  } on MssqlException catch (error) {
    if (!duplicateKeyIs(error, key.indexName)) rethrow;
    if (sessionIsDoomed(session)) {
      throw MssqlUniqueConflictException(
        table: binding.qualifiedName,
        indexName: key.indexName,
        reason:
            'the insert collided and the transaction is doomed, so the '
            'winning row cannot be read back. Roll it back and retry.',
      );
    }
    return _readUniqueWinner(
      key,
      restoreExisting: restoreExisting,
      includeDeleted: includeDeleted,
    );
  }
}