insertOrReplace method
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;
}