Summary
runBasicLinksQueryForIds in lib/database/database.dart builds a single to_id IN (…) query over the full merged wave set. When many concurrent callers coalesce in one microtask (e.g. the DailyOS prefetch window), the merged set can exceed SQLite's default SQLITE_MAX_VARIABLE_NUMBER (999), causing a runtime error.
Root cause
The method currently passes ids.toList() directly to the Drift query without chunking:
Future<List<EntryLink>> runBasicLinksQueryForIds(Set<String> ids) async {
final rows =
await (select(linkedEntries)..where(
(t) => t.toId.isIn(ids.toList()) & t.type.equals('BasicLink'),
))
.get();
return rows.map(entryLinkFromLinkedDbEntry).toList();
}
Proposed fix
Mirror the chunking pattern from runRatingsForTimeEntriesQueryForIds (added in PR #2964), using the existing _sqliteInListChunk = 500 constant:
Future<List<EntryLink>> runBasicLinksQueryForIds(Set<String> ids) async {
final idList = ids.toList(growable: false);
if (idList.length <= _sqliteInListChunk) {
final rows = await (select(linkedEntries)..where(
(t) => t.toId.isIn(idList) & t.type.equals('BasicLink'),
)).get();
return rows.map(entryLinkFromLinkedDbEntry).toList();
}
final combined = <EntryLink>[];
for (var i = 0; i < idList.length; i += _sqliteInListChunk) {
final end = (i + _sqliteInListChunk).clamp(0, idList.length);
final chunk = idList.sublist(i, end);
final rows = await (select(linkedEntries)..where(
(t) => t.toId.isIn(chunk) & t.type.equals('BasicLink'),
)).get();
combined.addAll(rows.map(entryLinkFromLinkedDbEntry));
}
return combined;
}
Since _PendingLinksWave.mergedIds is a Set, each toId appears in exactly one chunk, so the semantics of the coalesced wave are fully preserved.
References
Summary
runBasicLinksQueryForIdsinlib/database/database.dartbuilds a singleto_id IN (…)query over the full merged wave set. When many concurrent callers coalesce in one microtask (e.g. the DailyOS prefetch window), the merged set can exceed SQLite's defaultSQLITE_MAX_VARIABLE_NUMBER(999), causing a runtime error.Root cause
The method currently passes
ids.toList()directly to the Drift query without chunking:Proposed fix
Mirror the chunking pattern from
runRatingsForTimeEntriesQueryForIds(added in PR #2964), using the existing_sqliteInListChunk = 500constant:Since
_PendingLinksWave.mergedIdsis aSet, eachtoIdappears in exactly one chunk, so the semantics of the coalesced wave are fully preserved.References