diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index dabaea9910..9db4c91743 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1181,8 +1181,17 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, /* Project the new tuple version */ ExecProject(resultRelInfo->ri_onConflictSetProj, NULL); + /* + * Note that it is possible that the target tuple has been modified in + * this session, after the above heap_lock_tuple. We choose to not error + * out in that case, in line with ExecUpdate's treatment of similar + * cases. This can happen if an UPDATE is triggered from within + * ExecQual(), ExecWithCheckOptions() or ExecProject() above, e.g. by + * selecting from a wCTE in the ON CONFLICT's SET. + */ + /* Execute UPDATE with projection */ - *returning = ExecUpdate(&tuple.t_data->t_ctid, NULL, + *returning = ExecUpdate(&tuple.t_self, NULL, mtstate->mt_conflproj, planSlot, &mtstate->mt_epqstate, mtstate->ps.state, canSetTag); diff --git a/src/test/regress/expected/triggers.out b/src/test/regress/expected/triggers.out index dd4a99bff1..a7bf5dc159 100644 --- a/src/test/regress/expected/triggers.out +++ b/src/test/regress/expected/triggers.out @@ -1703,7 +1703,7 @@ create function upsert_after_func() $$ begin if (TG_OP = 'UPDATE') then - raise warning 'after update (old): %', new.*::text; + raise warning 'after update (old): %', old.*::text; raise warning 'after update (new): %', new.*::text; elsif (TG_OP = 'INSERT') then raise warning 'after insert (new): %', new.*::text; @@ -1724,7 +1724,7 @@ insert into upsert values(3, 'orange') on conflict (key) do update set color = ' WARNING: before insert (new): (3,orange) WARNING: before update (old): (3,"red trig modified") WARNING: before update (new): (3,"updated red trig modified") -WARNING: after update (old): (3,"updated red trig modified") +WARNING: after update (old): (3,"red trig modified") WARNING: after update (new): (3,"updated red trig modified") insert into upsert values(4, 'green') on conflict (key) do update set color = 'updated ' || upsert.color; WARNING: before insert (new): (4,green) @@ -1734,7 +1734,7 @@ insert into upsert values(5, 'purple') on conflict (key) do update set color = ' WARNING: before insert (new): (5,purple) WARNING: before update (old): (5,"green trig modified") WARNING: before update (new): (5,"updated green trig modified") -WARNING: after update (old): (5,"updated green trig modified") +WARNING: after update (old): (5,"green trig modified") WARNING: after update (new): (5,"updated green trig modified") insert into upsert values(6, 'white') on conflict (key) do update set color = 'updated ' || upsert.color; WARNING: before insert (new): (6,white) @@ -1744,7 +1744,7 @@ insert into upsert values(7, 'pink') on conflict (key) do update set color = 'up WARNING: before insert (new): (7,pink) WARNING: before update (old): (7,"white trig modified") WARNING: before update (new): (7,"updated white trig modified") -WARNING: after update (old): (7,"updated white trig modified") +WARNING: after update (old): (7,"white trig modified") WARNING: after update (new): (7,"updated white trig modified") insert into upsert values(8, 'yellow') on conflict (key) do update set color = 'updated ' || upsert.color; WARNING: before insert (new): (8,yellow) diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out index 2c9226c3db..137420d9b7 100644 --- a/src/test/regress/expected/with.out +++ b/src/test/regress/expected/with.out @@ -1875,8 +1875,9 @@ ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 'a' L WITH aa AS (SELECT 1 a, 2 b) INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); --- This shows an attempt to update an invisible row, which should really be --- reported as a cardinality violation, but it doesn't seem worth fixing: +-- Update a row more than once, in different parts of a wCTE. That is +-- an allowed, presumably very rare, edge case, but since it was +-- broken in the past, having a test seems worthwhile. WITH simpletup AS ( SELECT 2 k, 'Green' v), upsert_cte AS ( @@ -1886,7 +1887,10 @@ upsert_cte AS ( INSERT INTO z VALUES(2, 'Red') ON CONFLICT (k) DO UPDATE SET (k, v) = (SELECT k, v FROM upsert_cte WHERE upsert_cte.k = z.k) RETURNING k, v; -ERROR: attempted to update invisible tuple + k | v +---+--- +(0 rows) + DROP TABLE z; -- check that run to completion happens in proper ordering TRUNCATE TABLE y; diff --git a/src/test/regress/sql/triggers.sql b/src/test/regress/sql/triggers.sql index 9f66702cee..b6de1b3256 100644 --- a/src/test/regress/sql/triggers.sql +++ b/src/test/regress/sql/triggers.sql @@ -1215,7 +1215,7 @@ create function upsert_after_func() $$ begin if (TG_OP = 'UPDATE') then - raise warning 'after update (old): %', new.*::text; + raise warning 'after update (old): %', old.*::text; raise warning 'after update (new): %', new.*::text; elsif (TG_OP = 'INSERT') then raise warning 'after insert (new): %', new.*::text; diff --git a/src/test/regress/sql/with.sql b/src/test/regress/sql/with.sql index 3fd55f96b3..133ff0b195 100644 --- a/src/test/regress/sql/with.sql +++ b/src/test/regress/sql/with.sql @@ -838,8 +838,9 @@ WITH aa AS (SELECT 1 a, 2 b) INSERT INTO z VALUES(1, (SELECT b || ' insert' FROM aa WHERE a = 1 )) ON CONFLICT (k) DO UPDATE SET v = (SELECT b || ' update' FROM aa WHERE a = 1 LIMIT 1); --- This shows an attempt to update an invisible row, which should really be --- reported as a cardinality violation, but it doesn't seem worth fixing: +-- Update a row more than once, in different parts of a wCTE. That is +-- an allowed, presumably very rare, edge case, but since it was +-- broken in the past, having a test seems worthwhile. WITH simpletup AS ( SELECT 2 k, 'Green' v), upsert_cte AS (