insertOrReplace method

Future<int> insertOrReplace(
  1. Map<String, Object?> row
)

SQLite INSERT OR REPLACE: delete every existing row that would conflict (PK match or any UNIQUE-index match), then insert the new row. Returns the number of rows deleted in service of the insert (0 when no conflict existed).

Implementation

Future<int> insertOrReplace(Map<String, Object?> row) async {
  final pkVal = row[primaryKey.name];
  if (pkVal == null) {
    throw ArgumentError(
        'PagedTable.insertOrReplace: primary-key value is null');
  }
  final pkBytes = _encodePrimaryKey(pkVal);
  // Collect distinct PK values that need to be evicted. The set is
  // keyed by the JSON-encoded PK so int/string/etc. dedup correctly.
  final toDelete = <String, Object>{};
  if ((await _index.get(pkBytes)) != null) {
    toDelete[jsonEncode(pkVal)] = pkVal;
  }
  for (final si in _secondary.values) {
    if (!si.unique) continue;
    final prefix = _encodeSecondaryPrefix(si, row);
    if (prefix == null) continue;
    final upper = _bumpPrefix(prefix);
    // Buffer rowIds first to avoid mutating the tree mid-iteration.
    final rowIds = <int>[];
    await for (final e in si.btree
        .range(lower: prefix, lowerInclusive: true, upper: upper)) {
      rowIds.add(e.value);
    }
    for (final rid in rowIds) {
      final bytes = await _heap.get(rid);
      if (bytes == null) continue;
      final r = _decodeRow(bytes);
      final pk = r[primaryKey.name];
      if (pk == null) continue;
      toDelete[jsonEncode(pk)] = pk;
    }
  }
  for (final pk in toDelete.values) {
    await delete(pk);
  }
  await insert(row);
  return toDelete.length;
}