UPSERT under a BEFORE UPDATE trigger corrupts the index, so DELETE leaves a row
Author: RyanLin5967Created Sep 5, 2026Updated Sep 16, 2026
Labelsbugcompatibilitycorrectnessindexescorruption?triggersintegrity_check
Currently, an UPSERT whose DO UPDATE arm fires a BEFORE UPDATE trigger leaves the trigger's value behind in the index, so one row is counted twice and outlives its own DELETE. SQLite opening the file Turso wrote reports the same corrupt index.
CREATE TABLE t(a INTEGER PRIMARY KEY, c INTEGER);
CREATE TABLE u(a INTEGER PRIMARY KEY, c INTEGER);
CREATE INDEX i ON t(c);
CREATE INDEX j ON u(c);
INSERT INTO t VALUES(1,1);
INSERT INTO u VALUES(1,1);
CREATE TRIGGER tg BEFORE UPDATE ON t BEGIN UPDATE t SET c=c+1 WHERE a=NEW.a; END;
CREATE TRIGGER ug BEFORE UPDATE ON u BEGIN UPDATE u SET c=c+1 WHERE a=NEW.a; END;
INSERT INTO t VALUES(1,9) ON CONFLICT(a) DO UPDATE SET c=c+5;
SELECT count(*), group_concat(c) FROM (SELECT c FROM t WHERE c>0 ORDER BY c);
-- Turso: 2|2,6
-- SQLite: 1|6
-- one row is read twice, at the value the trigger wrote and at the value the row holds
DELETE FROM t WHERE a=1;
SELECT count(*) FROM t WHERE c>0;
-- Turso: 1
-- SQLite: 0
-- the table is empty and the stale entry outlives the row it pointed at
PRAGMA integrity_check;
-- Turso: wrong # of entries in index i
-- SQLite: ok
-- against a file rather than :memory:, sqlite3 opening it reports the same line
UPDATE u SET c=c+5 WHERE a=1;
SELECT count(*) FROM u WHERE c>0;
-- both 1, so the same trigger under a plain UPDATE keeps the index righttested on main & SQLite 3.50.4
Source: tursodatabase/turso