aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor
diff options
context:
space:
mode:
authorDean Rasheed <dean.a.rasheed@gmail.com>2023-09-30 10:55:24 +0100
committerDean Rasheed <dean.a.rasheed@gmail.com>2023-09-30 10:55:24 +0100
commit3c1a1af91d9504e4ce0319f48e83f7c2d5765969 (patch)
tree006c870d4a63fcf8dccbdacdb16e5b733cba7af1 /src/backend/executor
parentef595bf74459aac3d944714719bd33b87fbc0df1 (diff)
downloadpostgresql-3c1a1af91d9504e4ce0319f48e83f7c2d5765969.tar.gz
postgresql-3c1a1af91d9504e4ce0319f48e83f7c2d5765969.zip
Fix EvalPlanQual rechecking during MERGE.
Under some circumstances, concurrent MERGE operations could lead to inconsistent results, that varied according the plan chosen. This was caused by a lack of rowmarks on the source relation, which meant that EvalPlanQual rechecking was not guaranteed to return the same source tuples when re-running the join query. Fix by ensuring that preprocess_rowmarks() sets up PlanRowMarks for all non-target relations used in MERGE, in the same way that it does for UPDATE and DELETE. Per bug #18103. Back-patch to v15, where MERGE was introduced. Dean Rasheed, reviewed by Richard Guo. Discussion: https://postgr.es/m/18103-c4386baab8e355e3%40postgresql.org
Diffstat (limited to 'src/backend/executor')
-rw-r--r--src/backend/executor/README12
-rw-r--r--src/backend/executor/nodeModifyTable.c6
2 files changed, 9 insertions, 9 deletions
diff --git a/src/backend/executor/README b/src/backend/executor/README
index 17775a49e26..642d63be613 100644
--- a/src/backend/executor/README
+++ b/src/backend/executor/README
@@ -26,7 +26,7 @@ unnecessarily (for example, Sort does not rescan its input if no parameters
of the input have changed, since it can just reread its stored sorted data).
For a SELECT, it is only necessary to deliver the top-level result tuples
-to the client. For INSERT/UPDATE/DELETE, the actual table modification
+to the client. For INSERT/UPDATE/DELETE/MERGE, the actual table modification
operations happen in a top-level ModifyTable plan node. If the query
includes a RETURNING clause, the ModifyTable node delivers the computed
RETURNING rows as output, otherwise it returns nothing. Handling INSERT
@@ -353,8 +353,8 @@ EvalPlanQual (READ COMMITTED Update Checking)
For simple SELECTs, the executor need only pay attention to tuples that are
valid according to the snapshot seen by the current transaction (ie, they
were inserted by a previously committed transaction, and not deleted by any
-previously committed transaction). However, for UPDATE and DELETE it is not
-cool to modify or delete a tuple that's been modified by an open or
+previously committed transaction). However, for UPDATE, DELETE, and MERGE it
+is not cool to modify or delete a tuple that's been modified by an open or
concurrently-committed transaction. If we are running in SERIALIZABLE
isolation level then we just raise an error when this condition is seen to
occur. In READ COMMITTED isolation level, we must work a lot harder.
@@ -378,14 +378,14 @@ we're doing UPDATE). If no tuple is returned, then the modified tuple(s)
fail the quals, so we ignore the current result tuple and continue the
original query.
-In UPDATE/DELETE, only the target relation needs to be handled this way.
+In UPDATE/DELETE/MERGE, only the target relation needs to be handled this way.
In SELECT FOR UPDATE, there may be multiple relations flagged FOR UPDATE,
so we obtain lock on the current tuple version in each such relation before
executing the recheck.
It is also possible that there are relations in the query that are not
-to be locked (they are neither the UPDATE/DELETE target nor specified to
-be locked in SELECT FOR UPDATE/SHARE). When re-running the test query
+to be locked (they are neither the UPDATE/DELETE/MERGE target nor specified
+to be locked in SELECT FOR UPDATE/SHARE). When re-running the test query
we want to use the same rows from these relations that were joined to
the locked rows. For ordinary relations this can be implemented relatively
cheaply by including the row TID in the join outputs and re-fetching that
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index daf48dc0f0e..4f79be62b3c 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -4277,9 +4277,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
/*
* If we have any secondary relations in an UPDATE or DELETE, they need to
- * be treated like non-locked relations in SELECT FOR UPDATE, ie, the
- * EvalPlanQual mechanism needs to be told about them. Locate the
- * relevant ExecRowMarks.
+ * be treated like non-locked relations in SELECT FOR UPDATE, i.e., the
+ * EvalPlanQual mechanism needs to be told about them. This also goes for
+ * the source relations in a MERGE. Locate the relevant ExecRowMarks.
*/
arowmarks = NIL;
foreach(l, node->rowMarks)