From 883ab705f996051c2912aa5d0fdd530026f0e5b1 Mon Sep 17 00:00:00 2001 From: Claude Lin & Lay Date: Wed, 29 Apr 2026 02:45:05 +0900 Subject: [PATCH] fix(fts): regenerate AFTER triggers to force re-binding after 0003 (#135) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0003 で search_docs_code_fts table を DROP+CREATE 後も Worker が SQLITE_CORRUPT_VTAB を継続報告。trigger references の再解決のため AFTER INSERT/UPDATE/DELETE trigger を DROP + CREATE で再宣言する。 Trigger は declarative でデータを持たないため、再宣言は非破壊操作。 Body は 0001 と byte-for-byte 同一で、宣言タイミングのみが変わる。 適用は merge 後に D1 console もしくは `wrangler d1 migrations apply` で別途実行する。 Refs #135 --- migrations/0004_fts5_triggers_regen.sql | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 migrations/0004_fts5_triggers_regen.sql diff --git a/migrations/0004_fts5_triggers_regen.sql b/migrations/0004_fts5_triggers_regen.sql new file mode 100644 index 0000000..d7cb785 --- /dev/null +++ b/migrations/0004_fts5_triggers_regen.sql @@ -0,0 +1,57 @@ +-- 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;