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 15:15:32 Functions: 100.0 % 4 4 1 3
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           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 */
      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                 : 
      47            6987 :     CHECK_FOR_INTERRUPTS();
      48                 : 
      49                 :     /*
      50                 :      * get information from the node
      51                 :      */
      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:
      59            7038 :     slot = ExecProcNode(outerPlan);
      60                 : 
      61            7038 :     if (TupIsNull(slot))
      62                 :     {
      63                 :         /* Release any resources held by EPQ mechanism before exiting */
      64            1223 :         EvalPlanQualEnd(&node->lr_epqstate);
      65            1223 :         return NULL;
      66                 :     }
      67                 : 
      68                 :     /* We don't need EvalPlanQual unless we get updated tuple version(s) */
      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                 :      */
      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;
      84            6187 :         int         lockflags = 0;
      85                 :         TM_Result   test;
      86                 :         TupleTableSlot *markSlot;
      87                 : 
      88                 :         /* clear any leftover test tuple for this rel */
      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 */
      93            6187 :         if (erm->rti != erm->prti)
      94                 :         {
      95                 :             Oid         tableoid;
      96                 : 
      97             599 :             datum = ExecGetJunkAttribute(slot,
      98             599 :                                          aerm->toidAttNo,
      99                 :                                          &isNull);
     100                 :             /* shouldn't ever get a null result... */
     101             599 :             if (isNull)
     102 UBC           0 :                 elog(ERROR, "tableoid is NULL");
     103 CBC         599 :             tableoid = DatumGetObjectId(datum);
     104                 : 
     105             599 :             Assert(OidIsValid(erm->relid));
     106             599 :             if (tableoid != erm->relid)
     107                 :             {
     108                 :                 /* this child is inactive right now */
     109             125 :                 erm->ermActive = false;
     110             125 :                 ItemPointerSetInvalid(&(erm->curCtid));
     111             125 :                 ExecClearTuple(markSlot);
     112             125 :                 continue;
     113                 :             }
     114                 :         }
     115            6062 :         erm->ermActive = true;
     116                 : 
     117                 :         /* fetch the tuple's ctid */
     118            6062 :         datum = ExecGetJunkAttribute(slot,
     119            6062 :                                      aerm->ctidAttNo,
     120                 :                                      &isNull);
     121                 :         /* shouldn't ever get a null result... */
     122            6062 :         if (isNull)
     123 UBC           0 :             elog(ERROR, "ctid is NULL");
     124                 : 
     125                 :         /* requests for foreign tables must be passed to their FDW */
     126 CBC        6062 :         if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
     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                 : 
     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 */
     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 */
     161 CBC        6062 :         tid = *((ItemPointer) DatumGetPointer(datum));
     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;
     176 UBC           0 :             default:
     177               0 :                 elog(ERROR, "unsupported rowmark type");
     178                 :                 lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
     179                 :                 break;
     180                 :         }
     181                 : 
     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")));
     230 UBC           0 :                 elog(ERROR, "unexpected table_tuple_lock status: %u",
     231                 :                      test);
     232                 :                 break;
     233                 : 
     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                 : 
     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 */
     252 CBC        5990 :         erm->curCtid = tid;
     253                 :     }
     254                 : 
     255                 :     /*
     256                 :      * If we need to do EvalPlanQual testing, do so.
     257                 :      */
     258            5743 :     if (epq_needed)
     259                 :     {
     260                 :         /* Initialize EPQ machinery */
     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                 :          */
     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 */
     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;
     295            3511 :     Plan       *outerPlan = outerPlan(node);
     296                 :     List       *epq_arowmarks;
     297                 :     ListCell   *lc;
     298                 : 
     299                 :     /* check for unsupported flags */
     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;
     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                 :      */
     320            3511 :     ExecInitResultTypeTL(&lrstate->ps);
     321                 : 
     322                 :     /*
     323                 :      * then initialize outer plan
     324                 :      */
     325            3511 :     outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
     326                 : 
     327                 :     /* node returns unmodified slots from the outer plan */
     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                 :      */
     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                 :      */
     343            3511 :     lrstate->lr_arowMarks = NIL;
     344            3511 :     epq_arowmarks = NIL;
     345            7958 :     foreach(lc, node->rowMarks)
     346                 :     {
     347            4447 :         PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
     348                 :         ExecRowMark *erm;
     349                 :         ExecAuxRowMark *aerm;
     350                 : 
     351                 :         /* ignore "parent" rowmarks; they are irrelevant at runtime */
     352            4447 :         if (rc->isParent)
     353             723 :             continue;
     354                 : 
     355                 :         /* find ExecRowMark and build ExecAuxRowMark */
     356            3724 :         erm = ExecFindRowMark(estate, rc->rti, false);
     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                 :          */
     365            3724 :         if (RowMarkRequiresRowShareLock(erm->markType))
     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                 : 
     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 */
     389            3468 :     EvalPlanQualEnd(&node->lr_epqstate);
     390            3468 :     ExecEndNode(outerPlanState(node));
     391            3468 : }
     392                 : 
     393                 : 
     394                 : void
     395               8 : ExecReScanLockRows(LockRowsState *node)
     396                 : {
     397 GNC           8 :     PlanState  *outerPlan = outerPlanState(node);
     398                 : 
     399 ECB             :     /*
     400                 :      * if chgParam of subnode is not null then plan will be re-scanned by
     401                 :      * first ExecProcNode.
     402                 :      */
     403 GNC           8 :     if (outerPlan->chgParam == NULL)
     404 UNC           0 :         ExecReScan(outerPlan);
     405 CBC           8 : }
        

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