* Delete unresolved-ref rows by row id — the precise cleanup for refs a * resolution pass actually processed. The key-tuple variant above also * deletes SIBLING rows (same caller calling the same callee at other lines) * that a later batch hasn't attempted yet, so when a batch boundary spli
(rowIds: number[])
| 2256 | * created (#1269). |
| 2257 | */ |
| 2258 | deleteReferencesByRowIds(rowIds: number[]): number { |
| 2259 | if (rowIds.length === 0) return 0; |
| 2260 | // One transaction for all chunks (each chunk was previously its own |
| 2261 | // implicit transaction = its own WAL commit — measurable on 100k+-ref |
| 2262 | // resolution persists), and the full-size chunk statement is cached so |
| 2263 | // repeat calls skip the re-prepare; only the final partial chunk (if any) |
| 2264 | // prepares ad hoc. Returns rows actually removed (summed `changes`) for |
| 2265 | // the batched loop's non-progress guard (§7a.2). |
| 2266 | let changed = 0; |
| 2267 | this.db.transaction(() => { |
| 2268 | for (let i = 0; i < rowIds.length; i += SQLITE_PARAM_CHUNK_SIZE) { |
| 2269 | const chunk = rowIds.slice(i, i + SQLITE_PARAM_CHUNK_SIZE); |
| 2270 | if (chunk.length === SQLITE_PARAM_CHUNK_SIZE) { |
| 2271 | if (!this.stmts.deleteRefsByRowIdsFull) { |
| 2272 | const placeholders = new Array(SQLITE_PARAM_CHUNK_SIZE).fill('?').join(','); |
| 2273 | this.stmts.deleteRefsByRowIdsFull = this.db.prepare( |
| 2274 | `DELETE FROM unresolved_refs WHERE id IN (${placeholders})` |
| 2275 | ); |
| 2276 | } |
| 2277 | changed += this.stmts.deleteRefsByRowIdsFull.run(...chunk).changes; |
| 2278 | } else { |
| 2279 | const placeholders = chunk.map(() => '?').join(','); |
| 2280 | changed += this.db.prepare(`DELETE FROM unresolved_refs WHERE id IN (${placeholders})`).run(...chunk).changes; |
| 2281 | } |
| 2282 | } |
| 2283 | })(); |
| 2284 | return changed; |
| 2285 | } |
| 2286 | |
| 2287 | /** |
| 2288 | * Mark refs a completed resolution pass could not resolve as status='failed' |
no test coverage detected