]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Evade extra table_tuple_fetch_row_version() in ExecUpdate()/ExecDelete()
authorAlexander Korotkov <akorotkov@postgresql.org>
Wed, 22 Mar 2023 21:12:00 +0000 (00:12 +0300)
committerAlexander Korotkov <akorotkov@postgresql.org>
Wed, 22 Mar 2023 21:26:59 +0000 (00:26 +0300)
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
src/backend/executor/nodeModifyTable.c

index 3a673895082a170914bcadd6da479bb1afc349b5..93ebfdbb0d83080ea4676d7a4fd07c3d1e80f887 100644 (file)
@@ -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;