aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/nodeModifyTable.c
diff options
context:
space:
mode:
authorAlexander Korotkov <akorotkov@postgresql.org>2023-03-23 00:12:00 +0300
committerAlexander Korotkov <akorotkov@postgresql.org>2023-03-23 00:26:59 +0300
commit764da7710bf68eebb2c0facb2f871bc3c7a705b6 (patch)
tree73541ba0a7ed28fdb43f856d55e2590a72222495 /src/backend/executor/nodeModifyTable.c
parentc75a623304bc361b4456f916d455ea175ffd8055 (diff)
downloadpostgresql-764da7710bf68eebb2c0facb2f871bc3c7a705b6.tar.gz
postgresql-764da7710bf68eebb2c0facb2f871bc3c7a705b6.zip
Evade extra table_tuple_fetch_row_version() in ExecUpdate()/ExecDelete()
When we lock tuple using table_tuple_lock() then we at the same time fetch the locked tuple to the slot. In this case we can skip extra table_tuple_fetch_row_version() thank to we've already fetched the 'old' tuple and nobody can change it concurrently since it's locked. Discussion: https://postgr.es/m/CAPpHfdua-YFw3XTprfutzGp28xXLigFtzNbuFY8yPhqeq6X5kg%40mail.gmail.com Reviewed-by: Aleksander Alekseev, Pavel Borisov, Vignesh C, Mason Sharp Reviewed-by: Andres Freund, Chris Travers
Diffstat (limited to 'src/backend/executor/nodeModifyTable.c')
-rw-r--r--src/backend/executor/nodeModifyTable.c48
1 files changed, 35 insertions, 13 deletions
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index 3a673895082..93ebfdbb0d8 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1559,6 +1559,22 @@ ldelete:
{
case TM_Ok:
Assert(context->tmfd.traversed);
+
+ /*
+ * Save locked tuple for further processing of
+ * RETURNING clause.
+ */
+ if (processReturning &&
+ resultRelInfo->ri_projectReturning &&
+ !resultRelInfo->ri_FdwRoutine)
+ {
+ TupleTableSlot *returningSlot;
+
+ returningSlot = ExecGetReturningSlot(estate, resultRelInfo);
+ ExecCopySlot(returningSlot, inputslot);
+ ExecMaterializeSlot(returningSlot);
+ }
+
epqslot = EvalPlanQual(context->epqstate,
resultRelationDesc,
resultRelInfo->ri_RangeTableIndex,
@@ -1673,12 +1689,17 @@ ldelete:
}
else
{
+ /*
+ * Tuple can be already fetched to the returning slot in case
+ * we've previously locked it. Fetch the tuple only if the slot
+ * is empty.
+ */
slot = ExecGetReturningSlot(estate, resultRelInfo);
if (oldtuple != NULL)
{
ExecForceStoreHeapTuple(oldtuple, slot, false);
}
- else
+ else if (TupIsNull(slot))
{
if (!table_tuple_fetch_row_version(resultRelationDesc, tupleid,
SnapshotAny, slot))
@@ -2393,6 +2414,19 @@ redo_act:
case TM_Ok:
Assert(context->tmfd.traversed);
+ /* Make sure ri_oldTupleSlot is initialized. */
+ if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
+ ExecInitUpdateProjection(context->mtstate,
+ resultRelInfo);
+
+ /*
+ * Save the locked tuple for further calculation
+ * of the new tuple.
+ */
+ oldSlot = resultRelInfo->ri_oldTupleSlot;
+ ExecCopySlot(oldSlot, inputslot);
+ ExecMaterializeSlot(oldSlot);
+
epqslot = EvalPlanQual(context->epqstate,
resultRelationDesc,
resultRelInfo->ri_RangeTableIndex,
@@ -2401,18 +2435,6 @@ redo_act:
/* Tuple not passing quals anymore, exiting... */
return NULL;
- /* Make sure ri_oldTupleSlot is initialized. */
- if (unlikely(!resultRelInfo->ri_projectNewInfoValid))
- ExecInitUpdateProjection(context->mtstate,
- resultRelInfo);
-
- /* Fetch the most recent version of old tuple. */
- oldSlot = resultRelInfo->ri_oldTupleSlot;
- if (!table_tuple_fetch_row_version(resultRelationDesc,
- tupleid,
- SnapshotAny,
- oldSlot))
- elog(ERROR, "failed to fetch tuple being updated");
slot = ExecGetUpdateNewTuple(resultRelInfo,
epqslot, oldSlot);
goto redo_act;