/* 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);
$$
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;
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)
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)
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)
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 (
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;
$$
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;
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 (