Skip to content

UPDATE RETURNING correlated subquery uses pre-UPDATE values #5493

@LeMikaelF

Description

@LeMikaelF

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 modified

Per 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.

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions