LCOV - differential code coverage report
Current view: top level - src/backend/executor - nodeLockRows.c (source / functions) Coverage Total Hit UBC CBC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 83.6 % 128 107 21 107
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 4 4 4
Baseline: 16@8cea358b128 Branches: 60.0 % 90 54 36 54
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (240..) days: 83.6 % 128 107 21 107
Function coverage date bins:
(240..) days: 100.0 % 4 4 4
Branch coverage date bins:
(240..) days: 60.0 % 90 54 36 54

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

Generated by: LCOV version 2.1-beta2-3-g6141622