-
Notifications
You must be signed in to change notification settings - Fork 739
Open
Description
Description
When UPDATE RETURNING includes a correlated subquery, the subquery resolves column references to the pre-UPDATE values, even though direct column references in RETURNING correctly show post-UPDATE values. DELETE RETURNING subqueries work correctly.
Reproducer
CREATE TABLE t1(a INTEGER PRIMARY KEY, b INTEGER);
CREATE TABLE lookup(k INTEGER PRIMARY KEY, v TEXT);
INSERT INTO t1 VALUES(1, 10);
INSERT INTO lookup VALUES(10, 'old_value'), (20, 'new_value');
-- Direct column shows new b=20, but subquery WHERE k = b matches old b=10
UPDATE t1 SET b = 20 WHERE a = 1
RETURNING b, (SELECT v FROM lookup WHERE k = b) as looked_up;
-- Turso: 20|old_value
-- SQLite: 20|new_value
-- Multi-row variant: all subqueries see old values
CREATE TABLE t2(a INTEGER PRIMARY KEY, b INTEGER);
INSERT INTO t2 VALUES(1, 10), (2, 20), (3, 30);
UPDATE t2 SET b = b * 10
RETURNING a, b, (SELECT v FROM lookup WHERE k = b) as label;
-- Turso: 1|100|old_value, 2|200|new_value, 3|300|NULL
-- SQLite: 1|100|NULL, 2|200|NULL, 3|300|NULL
-- (subquery matched old values k=10,20,30 instead of new values k=100,200,300)
-- Unmodified columns work correctly:
UPDATE t2 SET b = 999
RETURNING a, (SELECT v FROM lookup WHERE k = a) as by_pk;
-- Works correctly because 'a' is not modifiedPer SQLite documentation: "For INSERT and UPDATE statements, references to columns in the table being modified refer to the value of that column after the change has been applied."
The RETURNING subquery evaluation (including Column reads from the write cursor) happens before the Insert instruction that writes the updated row in the bytecode, so cursor reads get old values while register-based RETURNING column values have the new values.
This issue brought to you by Mikaël and Claude Code.
Reactions are currently unavailable