diff --git a/migrations/0002_fts5_rebuild.sql b/migrations/0002_fts5_rebuild.sql deleted file mode 100644 index 177d188..0000000 --- a/migrations/0002_fts5_rebuild.sql +++ /dev/null @@ -1,24 +0,0 @@ --- FTS5 virtual table rebuild — recover from SQLITE_CORRUPT_VTAB --- --- Layer = L4 Operations (sparse retrieval surface recovery) --- --- Context: --- Observed on 2026-04-24: `search` sparse path and FTS upserts both --- failed with `D1_ERROR: database disk image is malformed: SQLITE_CORRUPT --- (extended: SQLITE_CORRUPT_VTAB)`. The content-owner table `search_docs` --- was intact (1109 rows, direct bm25 queries via `wrangler d1 execute` --- succeeded), so only the FTS5 virtual tables were corrupt. --- --- Recovery: --- FTS5's built-in `'rebuild'` command re-populates the virtual table from --- the content-owner table (`search_docs`). The operation is idempotent and --- non-destructive — it reads every row of `search_docs` and re-indexes it --- through each tokenizer. On a healthy or empty FTS table it is effectively --- a no-op, so this migration is safe to re-run. --- --- Scope: --- Covers both FTS5 tables (nat + code). Triggers from 0001 keep subsequent --- upserts in sync automatically. - -INSERT INTO search_docs_nat_fts(search_docs_nat_fts) VALUES('rebuild'); -INSERT INTO search_docs_code_fts(search_docs_code_fts) VALUES('rebuild'); diff --git a/migrations/0003_fts5_code_recreate.sql b/migrations/0003_fts5_code_recreate.sql deleted file mode 100644 index 19921e0..0000000 --- a/migrations/0003_fts5_code_recreate.sql +++ /dev/null @@ -1,37 +0,0 @@ --- D1 FTS5 code_fts virtual table fresh recreate — recurring SQLITE_CORRUPT_VTAB --- --- Layer = L4 Operations (sparse retrieval surface recovery) --- --- Context: --- 2026-04-24: migration 0002 applied FTS5 'rebuild' to both nat_fts and code_fts --- to recover from SQLITE_CORRUPT_VTAB. --- 2026-04-28: corruption recurred on code_fts only (trigram tokenizer side). --- Enriched logs from PR #137 confirmed errorName=Error / --- D1_ERROR: database disk image is malformed: SQLITE_CORRUPT_VTAB on every diff --- upsert across all 5 polled repos. --- --- Recovery (more aggressive than 0002): --- DROP the corrupted virtual table and recreate it from scratch with the --- same definition as 0001, then repopulate via FTS5 'rebuild' which reads --- from the content-owner table (search_docs). --- --- Triggers from 0001 (trg_search_docs_ai/ad/au) reference search_docs_code_fts --- by name; they resume working as soon as the new table exists, so they do --- not need to be redefined. --- --- Scope: --- Affects code_fts only. nat_fts is untouched (no recurring corruption observed there). --- --- Idempotency: --- IF EXISTS / IF NOT EXISTS clauses keep the migration safe to re-run. - -DROP TABLE IF EXISTS search_docs_code_fts; - -CREATE VIRTUAL TABLE IF NOT EXISTS search_docs_code_fts USING fts5 ( - content, - tokenize = 'trigram case_sensitive 0', - content = 'search_docs', - content_rowid = 'rowid' -); - -INSERT INTO search_docs_code_fts(search_docs_code_fts) VALUES('rebuild'); diff --git a/migrations/0004_fts5_triggers_regen.sql b/migrations/0004_fts5_triggers_regen.sql deleted file mode 100644 index d7cb785..0000000 --- a/migrations/0004_fts5_triggers_regen.sql +++ /dev/null @@ -1,57 +0,0 @@ --- Force re-declaration of FTS5 sync triggers (axis 2 attempt for issue #135) --- --- Layer = L4 Operations (sparse retrieval surface recovery) --- --- Context: --- 2026-04-28: migration 0003 dropped+recreated search_docs_code_fts to --- recover from recurring SQLITE_CORRUPT_VTAB. After 0003 was merged AND --- applied via D1 console, production Worker continues to hit --- D1_ERROR: SQLITE_CORRUPT_VTAB on every diff upsert (tokenizer_kind=code). --- nat_fts surface is clean; code_fts surface persists corrupt across --- :30 pollDiffs cron iterations. --- --- Hypothesis: --- The AFTER INSERT/UPDATE/DELETE triggers from 0001 were compiled with --- references that may need re-resolution after the underlying virtual --- table was DROP+CREATEd. Re-declaring the triggers (DROP + CREATE) --- forces re-binding to the new search_docs_code_fts. --- --- Scope: --- Triggers carry no data (declarative), so this is non-destructive. --- Body is byte-for-byte identical to 0001; only the declaration --- timing changes. --- --- Idempotency: --- DROP IF EXISTS keeps the migration safe to re-run. - -DROP TRIGGER IF EXISTS trg_search_docs_ai; -DROP TRIGGER IF EXISTS trg_search_docs_ad; -DROP TRIGGER IF EXISTS trg_search_docs_au; - -CREATE TRIGGER trg_search_docs_ai AFTER INSERT ON search_docs -BEGIN - INSERT INTO search_docs_nat_fts(rowid, content) - SELECT new.rowid, new.content WHERE new.tokenizer_kind = 'nat'; - INSERT INTO search_docs_code_fts(rowid, content) - SELECT new.rowid, new.content WHERE new.tokenizer_kind = 'code'; -END; - -CREATE TRIGGER trg_search_docs_ad AFTER DELETE ON search_docs -BEGIN - INSERT INTO search_docs_nat_fts(search_docs_nat_fts, rowid, content) - VALUES('delete', old.rowid, old.content); - INSERT INTO search_docs_code_fts(search_docs_code_fts, rowid, content) - VALUES('delete', old.rowid, old.content); -END; - -CREATE TRIGGER trg_search_docs_au AFTER UPDATE ON search_docs -BEGIN - INSERT INTO search_docs_nat_fts(search_docs_nat_fts, rowid, content) - VALUES('delete', old.rowid, old.content); - INSERT INTO search_docs_code_fts(search_docs_code_fts, rowid, content) - VALUES('delete', old.rowid, old.content); - INSERT INTO search_docs_nat_fts(rowid, content) - SELECT new.rowid, new.content WHERE new.tokenizer_kind = 'nat'; - INSERT INTO search_docs_code_fts(rowid, content) - SELECT new.rowid, new.content WHERE new.tokenizer_kind = 'code'; -END;