LCOV - code coverage report
Current view: top level - src/backend/executor - nodeLockRows.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 93 109 85.3 %
Date: 2019-11-22 07:06:56 Functions: 4 4 100.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13