fix(db): chunk deleteResolvedReferences IN-list under the SQLite param limit (#1001) (#1023)

deleteResolvedReferences bound every id into a single unbounded
`IN (...)`, so a list longer than SQLITE_MAX_VARIABLE_NUMBER (32766 on
the bundled node:sqlite) threw "too many SQL variables" — the one IN-list
in queries.ts that #540 missed. It's reachable only through the exported
QueryBuilder (library use): the internal resolution path uses
deleteSpecificResolvedReferences, which binds per-row and is immune, so
the CLI/MCP indexing pipeline was never affected. Wrap it in the same
SQLITE_PARAM_CHUNK_SIZE loop every sibling query uses, and add a
regression test (33k ids, past the real 32766 ceiling) that throws
without the fix.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Colby Mchenry
2026-06-27 14:00:55 -05:00
committed by GitHub
co-authored by Claude Opus 4.8
parent f83a1ecc8e
commit 30dc303f4c
3 changed files with 55 additions and 2 deletions
+10 -2
View File
@@ -1731,8 +1731,16 @@ export class QueryBuilder {
*/
deleteResolvedReferences(fromNodeIds: string[]): void {
if (fromNodeIds.length === 0) return;
const placeholders = fromNodeIds.map(() => '?').join(',');
this.db.prepare(`DELETE FROM unresolved_refs WHERE from_node_id IN (${placeholders})`).run(...fromNodeIds);
// Chunk under SQLite's parameter limit, matching every other IN-list in
// this file. The internal resolution path uses deleteSpecificResolvedReferences
// instead, but QueryBuilder is part of the public API, so a library consumer
// passing more ids than SQLITE_MAX_VARIABLE_NUMBER (32766 on the bundled
// node:sqlite) would otherwise hit "too many SQL variables". (#540, #1001)
for (let i = 0; i < fromNodeIds.length; i += SQLITE_PARAM_CHUNK_SIZE) {
const chunk = fromNodeIds.slice(i, i + SQLITE_PARAM_CHUNK_SIZE);
const placeholders = chunk.map(() => '?').join(',');
this.db.prepare(`DELETE FROM unresolved_refs WHERE from_node_id IN (${placeholders})`).run(...chunk);
}
}
/**