LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeLockRows.c (source / functions) Coverage Total Hit UNC UBC GNC CBC EUB ECB
Current: Differential Code Coverage HEAD vs 15 Lines: 83.7 % 129 108 1 20 2 106 1 2
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 4 4 1 3
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (240..) days: 83.7 % 129 108 1 20 2 106 1
Legend: Lines: hit not hit Function coverage date bins:
(240..) days: 100.0 % 4 4 1 3

 Age         Owner                  TLA  Line data    Source code
                                  1                 : /*-------------------------------------------------------------------------
                                  2                 :  *
                                  3                 :  * nodeLockRows.c
                                  4                 :  *    Routines to handle FOR UPDATE/FOR SHARE row locking
                                  5                 :  *
                                  6                 :  * Portions Copyright (c) 1996-2023, PostgreSQL Global Development Group
                                  7                 :  * Portions Copyright (c) 1994, Regents of the University of California
                                  8                 :  *
                                  9                 :  *
                                 10                 :  * IDENTIFICATION
                                 11                 :  *    src/backend/executor/nodeLockRows.c
                                 12                 :  *
                                 13                 :  *-------------------------------------------------------------------------
                                 14                 :  */
                                 15                 : /*
                                 16                 :  * INTERFACE ROUTINES
                                 17                 :  *      ExecLockRows        - fetch locked rows
                                 18                 :  *      ExecInitLockRows    - initialize node and subnodes..
                                 19                 :  *      ExecEndLockRows     - shutdown node and subnodes
                                 20                 :  */
                                 21                 : 
                                 22                 : #include "postgres.h"
                                 23                 : 
                                 24                 : #include "access/tableam.h"
                                 25                 : #include "access/xact.h"
                                 26                 : #include "executor/executor.h"
                                 27                 : #include "executor/nodeLockRows.h"
                                 28                 : #include "foreign/fdwapi.h"
                                 29                 : #include "miscadmin.h"
                                 30                 : #include "utils/rel.h"
                                 31                 : 
                                 32                 : 
                                 33                 : /* ----------------------------------------------------------------
                                 34                 :  *      ExecLockRows
                                 35                 :  * ----------------------------------------------------------------
                                 36                 :  */
                                 37                 : static TupleTableSlot *         /* return: a tuple or NULL */
 2092 andres                     38 CBC        6987 : ExecLockRows(PlanState *pstate)
                                 39                 : {
                                 40            6987 :     LockRowsState *node = castNode(LockRowsState, pstate);
                                 41                 :     TupleTableSlot *slot;
                                 42                 :     EState     *estate;
                                 43                 :     PlanState  *outerPlan;
                                 44                 :     bool        epq_needed;
                                 45                 :     ListCell   *lc;
                                 46                 : 
 2084                            47            6987 :     CHECK_FOR_INTERRUPTS();
                                 48                 : 
                                 49                 :     /*
                                 50                 :      * get information from the node
                                 51                 :      */
 4927 tgl                        52            6987 :     estate = node->ps.state;
                                 53            6987 :     outerPlan = outerPlanState(node);
                                 54                 : 
                                 55                 :     /*
                                 56                 :      * Get next tuple from subplan, if any.
                                 57                 :      */
                                 58              51 : lnext:
 4913                            59            7038 :     slot = ExecProcNode(outerPlan);
                                 60                 : 
 4927                            61            7038 :     if (TupIsNull(slot))
                                 62                 :     {
                                 63                 :         /* Release any resources held by EPQ mechanism before exiting */
  668                            64            1223 :         EvalPlanQualEnd(&node->lr_epqstate);
 4927                            65            1223 :         return NULL;
                                 66                 :     }
                                 67                 : 
                                 68                 :     /* We don't need EvalPlanQual unless we get updated tuple version(s) */
 2889                            69            5815 :     epq_needed = false;
                                 70                 : 
                                 71                 :     /*
                                 72                 :      * Attempt to lock the source tuple(s).  (Note we only have locking
                                 73                 :      * rowmarks in lr_arowMarks.)
                                 74                 :      */
 4470                            75           11930 :     foreach(lc, node->lr_arowMarks)
                                 76                 :     {
                                 77            6187 :         ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
                                 78            6187 :         ExecRowMark *erm = aerm->rowmark;
                                 79                 :         Datum       datum;
                                 80                 :         bool        isNull;
                                 81                 :         ItemPointerData tid;
                                 82                 :         TM_FailureData tmfd;
                                 83                 :         LockTupleMode lockmode;
  367 alvherre                   84            6187 :         int         lockflags = 0;
                                 85                 :         TM_Result   test;
                                 86                 :         TupleTableSlot *markSlot;
                                 87                 : 
                                 88                 :         /* clear any leftover test tuple for this rel */
 1500 andres                     89            6187 :         markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
                                 90            6187 :         ExecClearTuple(markSlot);
                                 91                 : 
                                 92                 :         /* if child rel, must check whether it produced this row */
 4927 tgl                        93            6187 :         if (erm->rti != erm->prti)
                                 94                 :         {
                                 95                 :             Oid         tableoid;
                                 96                 : 
                                 97             599 :             datum = ExecGetJunkAttribute(slot,
 4470                            98             599 :                                          aerm->toidAttNo,
                                 99                 :                                          &isNull);
                                100                 :             /* shouldn't ever get a null result... */
 4927                           101             599 :             if (isNull)
 4927 tgl                       102 UBC           0 :                 elog(ERROR, "tableoid is NULL");
 4927 tgl                       103 CBC         599 :             tableoid = DatumGetObjectId(datum);
                                104                 : 
 2940                           105             599 :             Assert(OidIsValid(erm->relid));
                                106             599 :             if (tableoid != erm->relid)
                                107                 :             {
                                108                 :                 /* this child is inactive right now */
 2889                           109             125 :                 erm->ermActive = false;
 4927                           110             125 :                 ItemPointerSetInvalid(&(erm->curCtid));
 1478 andres                    111             125 :                 ExecClearTuple(markSlot);
 4927 tgl                       112             125 :                 continue;
                                113                 :             }
                                114                 :         }
 2889                           115            6062 :         erm->ermActive = true;
                                116                 : 
                                117                 :         /* fetch the tuple's ctid */
 4927                           118            6062 :         datum = ExecGetJunkAttribute(slot,
 4470                           119            6062 :                                      aerm->ctidAttNo,
                                120                 :                                      &isNull);
                                121                 :         /* shouldn't ever get a null result... */
 4927                           122            6062 :         if (isNull)
 4927 tgl                       123 UBC           0 :             elog(ERROR, "ctid is NULL");
                                124                 : 
                                125                 :         /* requests for foreign tables must be passed to their FDW */
 2889 tgl                       126 CBC        6062 :         if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
 2889 tgl                       127 UBC           0 :         {
                                128                 :             FdwRoutine *fdwroutine;
                                129               0 :             bool        updated = false;
                                130                 : 
                                131               0 :             fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
                                132                 :             /* this should have been checked already, but let's be safe */
                                133               0 :             if (fdwroutine->RefetchForeignRow == NULL)
                                134               0 :                 ereport(ERROR,
                                135                 :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                                136                 :                          errmsg("cannot lock rows in foreign table \"%s\"",
                                137                 :                                 RelationGetRelationName(erm->relation))));
                                138                 : 
 1500 andres                    139               0 :             fdwroutine->RefetchForeignRow(estate,
                                140                 :                                           erm,
                                141                 :                                           datum,
                                142                 :                                           markSlot,
                                143                 :                                           &updated);
                                144               0 :             if (TupIsNull(markSlot))
                                145                 :             {
                                146                 :                 /* couldn't get the lock, so skip this row */
 2889 tgl                       147               0 :                 goto lnext;
                                148                 :             }
                                149                 : 
                                150                 :             /*
                                151                 :              * if FDW says tuple was updated before getting locked, we need to
                                152                 :              * perform EPQ testing to see if quals are still satisfied
                                153                 :              */
                                154               0 :             if (updated)
                                155               0 :                 epq_needed = true;
                                156                 : 
                                157               0 :             continue;
                                158                 :         }
                                159                 : 
                                160                 :         /* okay, try to lock (and fetch) the tuple */
 1478 andres                    161 CBC        6062 :         tid = *((ItemPointer) DatumGetPointer(datum));
 3728 alvherre                  162            6062 :         switch (erm->markType)
                                163                 :         {
                                164            1938 :             case ROW_MARK_EXCLUSIVE:
                                165            1938 :                 lockmode = LockTupleExclusive;
                                166            1938 :                 break;
                                167              31 :             case ROW_MARK_NOKEYEXCLUSIVE:
                                168              31 :                 lockmode = LockTupleNoKeyExclusive;
                                169              31 :                 break;
                                170            1252 :             case ROW_MARK_SHARE:
                                171            1252 :                 lockmode = LockTupleShare;
                                172            1252 :                 break;
                                173            2841 :             case ROW_MARK_KEYSHARE:
                                174            2841 :                 lockmode = LockTupleKeyShare;
                                175            2841 :                 break;
 3728 alvherre                  176 UBC           0 :             default:
                                177               0 :                 elog(ERROR, "unsupported rowmark type");
                                178                 :                 lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
                                179                 :                 break;
                                180                 :         }
                                181                 : 
  367 alvherre                  182 CBC        6062 :         lockflags = TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS;
                                183            6062 :         if (!IsolationUsesXactSnapshot())
                                184            4221 :             lockflags |= TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
                                185                 : 
                                186            6062 :         test = table_tuple_lock(erm->relation, &tid, estate->es_snapshot,
                                187                 :                                 markSlot, estate->es_output_cid,
                                188                 :                                 lockmode, erm->waitPolicy,
                                189                 :                                 lockflags,
                                190                 :                                 &tmfd);
                                191                 : 
                                192            6049 :         switch (test)
                                193                 :         {
                                194              37 :             case TM_WouldBlock:
                                195                 :                 /* couldn't lock tuple in SKIP LOCKED mode */
                                196              37 :                 goto lnext;
                                197                 : 
                                198               3 :             case TM_SelfModified:
                                199                 : 
                                200                 :                 /*
                                201                 :                  * The target tuple was already updated or deleted by the
                                202                 :                  * current command, or by a later command in the current
                                203                 :                  * transaction.  We *must* ignore the tuple in the former
                                204                 :                  * case, so as to avoid the "Halloween problem" of repeated
                                205                 :                  * update attempts.  In the latter case it might be sensible
                                206                 :                  * to fetch the updated tuple instead, but doing so would
                                207                 :                  * require changing heap_update and heap_delete to not
                                208                 :                  * complain about updating "invisible" tuples, which seems
                                209                 :                  * pretty scary (table_tuple_lock will not complain, but few
                                210                 :                  * callers expect TM_Invisible, and we're not one of them). So
                                211                 :                  * for now, treat the tuple as deleted and do not process.
                                212                 :                  */
                                213               3 :                 goto lnext;
                                214                 : 
                                215            5990 :             case TM_Ok:
                                216                 : 
                                217                 :                 /*
                                218                 :                  * Got the lock successfully, the locked tuple saved in
                                219                 :                  * markSlot for, if needed, EvalPlanQual testing below.
                                220                 :                  */
                                221            5990 :                 if (tmfd.traversed)
                                222              28 :                     epq_needed = true;
                                223            5990 :                 break;
                                224                 : 
                                225              12 :             case TM_Updated:
                                226              12 :                 if (IsolationUsesXactSnapshot())
                                227              12 :                     ereport(ERROR,
                                228                 :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                229                 :                              errmsg("could not serialize access due to concurrent update")));
  367 alvherre                  230 UBC           0 :                 elog(ERROR, "unexpected table_tuple_lock status: %u",
                                231                 :                      test);
                                232                 :                 break;
                                233                 : 
  367 alvherre                  234 CBC           7 :             case TM_Deleted:
                                235               7 :                 if (IsolationUsesXactSnapshot())
                                236               2 :                     ereport(ERROR,
                                237                 :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
                                238                 :                              errmsg("could not serialize access due to concurrent update")));
                                239                 :                 /* tuple was deleted so don't return it */
                                240               5 :                 goto lnext;
                                241                 : 
  367 alvherre                  242 UBC           0 :             case TM_Invisible:
                                243               0 :                 elog(ERROR, "attempted to lock invisible tuple");
                                244                 :                 break;
                                245                 : 
                                246               0 :             default:
                                247               0 :                 elog(ERROR, "unrecognized table_tuple_lock status: %u",
                                248                 :                      test);
                                249                 :         }
                                250                 : 
                                251                 :         /* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
 1478 andres                    252 CBC        5990 :         erm->curCtid = tid;
                                253                 :     }
                                254                 : 
                                255                 :     /*
                                256                 :      * If we need to do EvalPlanQual testing, do so.
                                257                 :      */
 2889 tgl                       258            5743 :     if (epq_needed)
                                259                 :     {
                                260                 :         /* Initialize EPQ machinery */
 1312 andres                    261              27 :         EvalPlanQualBegin(&node->lr_epqstate);
                                262                 : 
                                263                 :         /*
                                264                 :          * To fetch non-locked source rows the EPQ logic needs to access junk
                                265                 :          * columns from the tuple being tested.
                                266                 :          */
 4913 tgl                       267              27 :         EvalPlanQualSetSlot(&node->lr_epqstate, slot);
                                268                 : 
                                269                 :         /*
                                270                 :          * And finally we can re-evaluate the tuple.
                                271                 :          */
                                272              27 :         slot = EvalPlanQualNext(&node->lr_epqstate);
                                273              27 :         if (TupIsNull(slot))
                                274                 :         {
                                275                 :             /* Updated tuple fails qual, so ignore it and go on */
                                276               6 :             goto lnext;
                                277                 :         }
                                278                 :     }
                                279                 : 
                                280                 :     /* Got all locks, so return the current tuple */
 4927                           281            5737 :     return slot;
                                282                 : }
                                283                 : 
                                284                 : /* ----------------------------------------------------------------
                                285                 :  *      ExecInitLockRows
                                286                 :  *
                                287                 :  *      This initializes the LockRows node state structures and
                                288                 :  *      the node's subplan.
                                289                 :  * ----------------------------------------------------------------
                                290                 :  */
                                291                 : LockRowsState *
                                292            3511 : ExecInitLockRows(LockRows *node, EState *estate, int eflags)
                                293                 : {
                                294                 :     LockRowsState *lrstate;
 4913                           295            3511 :     Plan       *outerPlan = outerPlan(node);
                                296                 :     List       *epq_arowmarks;
                                297                 :     ListCell   *lc;
                                298                 : 
                                299                 :     /* check for unsupported flags */
 4927                           300            3511 :     Assert(!(eflags & EXEC_FLAG_MARK));
                                301                 : 
                                302                 :     /*
                                303                 :      * create state structure
                                304                 :      */
                                305            3511 :     lrstate = makeNode(LockRowsState);
                                306            3511 :     lrstate->ps.plan = (Plan *) node;
                                307            3511 :     lrstate->ps.state = estate;
 2092 andres                    308            3511 :     lrstate->ps.ExecProcNode = ExecLockRows;
                                309                 : 
                                310                 :     /*
                                311                 :      * Miscellaneous initialization
                                312                 :      *
                                313                 :      * LockRows nodes never call ExecQual or ExecProject, therefore no
                                314                 :      * ExprContext is needed.
                                315                 :      */
                                316                 : 
                                317                 :     /*
                                318                 :      * Initialize result type.
                                319                 :      */
 1612                           320            3511 :     ExecInitResultTypeTL(&lrstate->ps);
                                321                 : 
                                322                 :     /*
                                323                 :      * then initialize outer plan
                                324                 :      */
 4927 tgl                       325            3511 :     outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
                                326                 : 
                                327                 :     /* node returns unmodified slots from the outer plan */
 1606 andres                    328            3511 :     lrstate->ps.resultopsset = true;
                                329            3511 :     lrstate->ps.resultops = ExecGetResultSlotOps(outerPlanState(lrstate),
                                330                 :                                                  &lrstate->ps.resultopsfixed);
                                331                 : 
                                332                 :     /*
                                333                 :      * LockRows nodes do no projections, so initialize projection info for
                                334                 :      * this node appropriately
                                335                 :      */
 4927 tgl                       336            3511 :     lrstate->ps.ps_ProjInfo = NULL;
                                337                 : 
                                338                 :     /*
                                339                 :      * Locate the ExecRowMark(s) that this node is responsible for, and
                                340                 :      * construct ExecAuxRowMarks for them.  (InitPlan should already have
                                341                 :      * built the global list of ExecRowMarks.)
                                342                 :      */
 4470                           343            3511 :     lrstate->lr_arowMarks = NIL;
                                344            3511 :     epq_arowmarks = NIL;
 4927                           345            7958 :     foreach(lc, node->rowMarks)
                                346                 :     {
 2190                           347            4447 :         PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
                                348                 :         ExecRowMark *erm;
                                349                 :         ExecAuxRowMark *aerm;
                                350                 : 
                                351                 :         /* ignore "parent" rowmarks; they are irrelevant at runtime */
 4927                           352            4447 :         if (rc->isParent)
                                353             723 :             continue;
                                354                 : 
                                355                 :         /* find ExecRowMark and build ExecAuxRowMark */
 2889                           356            3724 :         erm = ExecFindRowMark(estate, rc->rti, false);
 4470                           357            3724 :         aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
                                358                 : 
                                359                 :         /*
                                360                 :          * Only locking rowmarks go into our own list.  Non-locking marks are
                                361                 :          * passed off to the EvalPlanQual machinery.  This is because we don't
                                362                 :          * want to bother fetching non-locked rows unless we actually have to
                                363                 :          * do an EPQ recheck.
                                364                 :          */
 4913                           365            3724 :         if (RowMarkRequiresRowShareLock(erm->markType))
 4470                           366            3590 :             lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
                                367                 :         else
                                368             134 :             epq_arowmarks = lappend(epq_arowmarks, aerm);
                                369                 :     }
                                370                 : 
                                371                 :     /* Now we have the info needed to set up EPQ state */
                                372            3511 :     EvalPlanQualInit(&lrstate->lr_epqstate, estate,
                                373                 :                      outerPlan, epq_arowmarks, node->epqParam);
                                374                 : 
 4927                           375            3511 :     return lrstate;
                                376                 : }
                                377                 : 
                                378                 : /* ----------------------------------------------------------------
                                379                 :  *      ExecEndLockRows
                                380                 :  *
                                381                 :  *      This shuts down the subplan and frees resources allocated
                                382                 :  *      to this node.
                                383                 :  * ----------------------------------------------------------------
                                384                 :  */
                                385                 : void
                                386            3468 : ExecEndLockRows(LockRowsState *node)
                                387                 : {
                                388                 :     /* We may have shut down EPQ already, but no harm in another call */
 4913                           389            3468 :     EvalPlanQualEnd(&node->lr_epqstate);
 4927                           390            3468 :     ExecEndNode(outerPlanState(node));
                                391            3468 : }
                                392                 : 
                                393                 : 
                                394                 : void
 4654                           395               8 : ExecReScanLockRows(LockRowsState *node)
                                396                 : {
  276 tgl                       397 GNC           8 :     PlanState  *outerPlan = outerPlanState(node);
                                398                 : 
 4927 tgl                       399 ECB             :     /*
                                400                 :      * if chgParam of subnode is not null then plan will be re-scanned by
                                401                 :      * first ExecProcNode.
                                402                 :      */
  276 tgl                       403 GNC           8 :     if (outerPlan->chgParam == NULL)
  276 tgl                       404 UNC           0 :         ExecReScan(outerPlan);
 4927 tgl                       405 CBC           8 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a