|
| 1 | +-- Fix #48: switch articles_fts from a contentless FTS5 table to an |
| 2 | +-- external-content table (content='article_localizations', |
| 3 | +-- content_rowid='rowid'). |
| 4 | +-- |
| 5 | +-- The contentless pattern from migration 0002 requires the |
| 6 | +-- ('delete', old.rowid, …all column values…) tuple to **exactly |
| 7 | +-- match** what's stored in the index. Any drift — different |
| 8 | +-- normalization, trailing newline, prior failed REPLACE — makes the |
| 9 | +-- delete-trigger fail with SQLITE_ERROR. UPDATE inherits that fault. |
| 10 | +-- After-effect on the CMS: editors couldn't update or delete an |
| 11 | +-- article_localizations row once any drift had occurred. |
| 12 | +-- |
| 13 | +-- The external-content pattern lets the trigger reference rowid |
| 14 | +-- alone: |
| 15 | +-- INSERT INTO articles_fts(articles_fts, rowid) VALUES('delete', old.rowid); |
| 16 | +-- — no column values to match against, so drift becomes a non-issue. |
| 17 | +-- And `rebuild` can recover from any inconsistency without dropping |
| 18 | +-- the table. |
| 19 | +-- |
| 20 | +-- Migration steps: |
| 21 | +-- 1. Drop the old triggers (must happen before dropping the table). |
| 22 | +-- 2. Drop the old virtual table. |
| 23 | +-- 3. Recreate as external-content. |
| 24 | +-- 4. Rebuild the index from article_localizations in one shot. |
| 25 | +-- 5. Recreate triggers using the simpler delete pattern. |
| 26 | + |
| 27 | +DROP TRIGGER IF EXISTS articles_fts_ai; |
| 28 | +--> statement-breakpoint |
| 29 | +DROP TRIGGER IF EXISTS articles_fts_ad; |
| 30 | +--> statement-breakpoint |
| 31 | +DROP TRIGGER IF EXISTS articles_fts_au; |
| 32 | +--> statement-breakpoint |
| 33 | +DROP TABLE IF EXISTS articles_fts; |
| 34 | +--> statement-breakpoint |
| 35 | + |
| 36 | +CREATE VIRTUAL TABLE IF NOT EXISTS articles_fts USING fts5( |
| 37 | + title, |
| 38 | + excerpt, |
| 39 | + body, |
| 40 | + locale UNINDEXED, |
| 41 | + article_id UNINDEXED, |
| 42 | + content = 'article_localizations', |
| 43 | + content_rowid = 'rowid', |
| 44 | + tokenize = 'unicode61 remove_diacritics 2' |
| 45 | +); |
| 46 | +--> statement-breakpoint |
| 47 | + |
| 48 | +-- Build the index from current state. With external-content, this |
| 49 | +-- reads from article_localizations directly (the `content =` link |
| 50 | +-- above tells FTS5 where to look). |
| 51 | +INSERT INTO articles_fts(articles_fts) VALUES('rebuild'); |
| 52 | +--> statement-breakpoint |
| 53 | + |
| 54 | +-- Triggers — simpler than before because external-content tables |
| 55 | +-- only need rowid for delete (the source-of-truth values come from |
| 56 | +-- the linked table itself). |
| 57 | + |
| 58 | +CREATE TRIGGER IF NOT EXISTS articles_fts_ai AFTER INSERT ON article_localizations BEGIN |
| 59 | + INSERT INTO articles_fts(rowid, title, excerpt, body, locale, article_id) |
| 60 | + VALUES (new.rowid, new.title, COALESCE(new.excerpt, ''), new.body, new.locale, new.article_id); |
| 61 | +END; |
| 62 | +--> statement-breakpoint |
| 63 | + |
| 64 | +CREATE TRIGGER IF NOT EXISTS articles_fts_ad AFTER DELETE ON article_localizations BEGIN |
| 65 | + INSERT INTO articles_fts(articles_fts, rowid) VALUES('delete', old.rowid); |
| 66 | +END; |
| 67 | +--> statement-breakpoint |
| 68 | + |
| 69 | +CREATE TRIGGER IF NOT EXISTS articles_fts_au AFTER UPDATE ON article_localizations BEGIN |
| 70 | + INSERT INTO articles_fts(articles_fts, rowid) VALUES('delete', old.rowid); |
| 71 | + INSERT INTO articles_fts(rowid, title, excerpt, body, locale, article_id) |
| 72 | + VALUES (new.rowid, new.title, COALESCE(new.excerpt, ''), new.body, new.locale, new.article_id); |
| 73 | +END; |
0 commit comments