LCOV - code coverage report
Current view: top level - src/backend/executor - nodeLockRows.c (source / functions) Hit Total Coverage
Test: PostgreSQL 12beta2 Lines: 91 110 82.7 %
Date: 2019-06-18 07:06:57 Functions: 3 4 75.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       11476 : ExecLockRows(PlanState *pstate)
      39             : {
      40       11476 :     LockRowsState *node = castNode(LockRowsState, pstate);
      41             :     TupleTableSlot *slot;
      42             :     EState     *estate;
      43             :     PlanState  *outerPlan;
      44             :     bool        epq_needed;
      45             :     ListCell   *lc;
      46             : 
      47       11476 :     CHECK_FOR_INTERRUPTS();
      48             : 
      49             :     /*
      50             :      * get information from the node
      51             :      */
      52       11476 :     estate = node->ps.state;
      53       11476 :     outerPlan = outerPlanState(node);
      54             : 
      55             :     /*
      56             :      * Get next tuple from subplan, if any.
      57             :      */
      58             : lnext:
      59       11568 :     slot = ExecProcNode(outerPlan);
      60             : 
      61       11568 :     if (TupIsNull(slot))
      62        1576 :         return NULL;
      63             : 
      64             :     /* We don't need EvalPlanQual unless we get updated tuple version(s) */
      65        9992 :     epq_needed = false;
      66             : 
      67             :     /*
      68             :      * Initialize EPQ machinery. Need to do that early because source tuples
      69             :      * are stored in slots initialized therein.
      70             :      */
      71        9992 :     EvalPlanQualBegin(&node->lr_epqstate, estate);
      72             : 
      73             :     /*
      74             :      * Attempt to lock the source tuple(s).  (Note we only have locking
      75             :      * rowmarks in lr_arowMarks.)
      76             :      */
      77       20530 :     foreach(lc, node->lr_arowMarks)
      78             :     {
      79       10678 :         ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
      80       10678 :         ExecRowMark *erm = aerm->rowmark;
      81             :         Datum       datum;
      82             :         bool        isNull;
      83             :         ItemPointerData tid;
      84             :         TM_FailureData tmfd;
      85             :         LockTupleMode lockmode;
      86       10678 :         int         lockflags = 0;
      87             :         TM_Result   test;
      88             :         TupleTableSlot *markSlot;
      89             : 
      90             :         /* clear any leftover test tuple for this rel */
      91       10678 :         markSlot = EvalPlanQualSlot(&node->lr_epqstate, erm->relation, erm->rti);
      92       10678 :         ExecClearTuple(markSlot);
      93             : 
      94             :         /* if child rel, must check whether it produced this row */
      95       10678 :         if (erm->rti != erm->prti)
      96             :         {
      97             :             Oid         tableoid;
      98             : 
      99         476 :             datum = ExecGetJunkAttribute(slot,
     100         476 :                                          aerm->toidAttNo,
     101             :                                          &isNull);
     102             :             /* shouldn't ever get a null result... */
     103         476 :             if (isNull)
     104           0 :                 elog(ERROR, "tableoid is NULL");
     105         476 :             tableoid = DatumGetObjectId(datum);
     106             : 
     107             :             Assert(OidIsValid(erm->relid));
     108         476 :             if (tableoid != erm->relid)
     109             :             {
     110             :                 /* this child is inactive right now */
     111         190 :                 erm->ermActive = false;
     112         190 :                 ItemPointerSetInvalid(&(erm->curCtid));
     113         190 :                 ExecClearTuple(markSlot);
     114         380 :                 continue;
     115             :             }
     116             :         }
     117       10488 :         erm->ermActive = true;
     118             : 
     119             :         /* fetch the tuple's ctid */
     120       10488 :         datum = ExecGetJunkAttribute(slot,
     121       10488 :                                      aerm->ctidAttNo,
     122             :                                      &isNull);
     123             :         /* shouldn't ever get a null result... */
     124       10488 :         if (isNull)
     125           0 :             elog(ERROR, "ctid is NULL");
     126             : 
     127             :         /* requests for foreign tables must be passed to their FDW */
     128       10488 :         if (erm->relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
     129             :         {
     130             :             FdwRoutine *fdwroutine;
     131           0 :             bool        updated = false;
     132             : 
     133           0 :             fdwroutine = GetFdwRoutineForRelation(erm->relation, false);
     134             :             /* this should have been checked already, but let's be safe */
     135           0 :             if (fdwroutine->RefetchForeignRow == NULL)
     136           0 :                 ereport(ERROR,
     137             :                         (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     138             :                          errmsg("cannot lock rows in foreign table \"%s\"",
     139             :                                 RelationGetRelationName(erm->relation))));
     140             : 
     141           0 :             fdwroutine->RefetchForeignRow(estate,
     142             :                                           erm,
     143             :                                           datum,
     144             :                                           markSlot,
     145             :                                           &updated);
     146           0 :             if (TupIsNull(markSlot))
     147             :             {
     148             :                 /* couldn't get the lock, so skip this row */
     149             :                 goto lnext;
     150             :             }
     151             : 
     152             :             /*
     153             :              * if FDW says tuple was updated before getting locked, we need to
     154             :              * perform EPQ testing to see if quals are still satisfied
     155             :              */
     156           0 :             if (updated)
     157           0 :                 epq_needed = true;
     158             : 
     159           0 :             continue;
     160             :         }
     161             : 
     162             :         /* okay, try to lock (and fetch) the tuple */
     163       10488 :         tid = *((ItemPointer) DatumGetPointer(datum));
     164       10488 :         switch (erm->markType)
     165             :         {
     166             :             case ROW_MARK_EXCLUSIVE:
     167        3680 :                 lockmode = LockTupleExclusive;
     168        3680 :                 break;
     169             :             case ROW_MARK_NOKEYEXCLUSIVE:
     170          50 :                 lockmode = LockTupleNoKeyExclusive;
     171          50 :                 break;
     172             :             case ROW_MARK_SHARE:
     173        2374 :                 lockmode = LockTupleShare;
     174        2374 :                 break;
     175             :             case ROW_MARK_KEYSHARE:
     176        4384 :                 lockmode = LockTupleKeyShare;
     177        4384 :                 break;
     178             :             default:
     179           0 :                 elog(ERROR, "unsupported rowmark type");
     180             :                 lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
     181             :                 break;
     182             :         }
     183             : 
     184       10488 :         lockflags = TUPLE_LOCK_FLAG_LOCK_UPDATE_IN_PROGRESS;
     185       10488 :         if (!IsolationUsesXactSnapshot())
     186        6906 :             lockflags |= TUPLE_LOCK_FLAG_FIND_LAST_VERSION;
     187             : 
     188       10488 :         test = table_tuple_lock(erm->relation, &tid, estate->es_snapshot,
     189             :                                 markSlot, estate->es_output_cid,
     190             :                                 lockmode, erm->waitPolicy,
     191             :                                 lockflags,
     192             :                                 &tmfd);
     193             : 
     194       10462 :         switch (test)
     195             :         {
     196             :             case TM_WouldBlock:
     197             :                 /* couldn't lock tuple in SKIP LOCKED mode */
     198          74 :                 goto lnext;
     199             : 
     200             :             case TM_SelfModified:
     201             : 
     202             :                 /*
     203             :                  * The target tuple was already updated or deleted by the
     204             :                  * current command, or by a later command in the current
     205             :                  * transaction.  We *must* ignore the tuple in the former
     206             :                  * case, so as to avoid the "Halloween problem" of repeated
     207             :                  * update attempts.  In the latter case it might be sensible
     208             :                  * to fetch the updated tuple instead, but doing so would
     209             :                  * require changing heap_update and heap_delete to not
     210             :                  * complain about updating "invisible" tuples, which seems
     211             :                  * pretty scary (table_tuple_lock will not complain, but few
     212             :                  * callers expect TM_Invisible, and we're not one of them). So
     213             :                  * for now, treat the tuple as deleted and do not process.
     214             :                  */
     215           4 :                 goto lnext;
     216             : 
     217             :             case TM_Ok:
     218             : 
     219             :                 /*
     220             :                  * Got the lock successfully, the locked tuple saved in
     221             :                  * markSlot for, if needed, EvalPlanQual testing below.
     222             :                  */
     223       10348 :                 if (tmfd.traversed)
     224          40 :                     epq_needed = true;
     225       10348 :                 break;
     226             : 
     227             :             case TM_Updated:
     228          24 :                 if (IsolationUsesXactSnapshot())
     229          24 :                     ereport(ERROR,
     230             :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
     231             :                              errmsg("could not serialize access due to concurrent update")));
     232           0 :                 elog(ERROR, "unexpected table_tuple_lock status: %u",
     233             :                      test);
     234             :                 break;
     235             : 
     236             :             case TM_Deleted:
     237          12 :                 if (IsolationUsesXactSnapshot())
     238           4 :                     ereport(ERROR,
     239             :                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
     240             :                              errmsg("could not serialize access due to concurrent update")));
     241             :                 /* tuple was deleted so don't return it */
     242           8 :                 goto lnext;
     243             : 
     244             :             case TM_Invisible:
     245           0 :                 elog(ERROR, "attempted to lock invisible tuple");
     246             :                 break;
     247             : 
     248             :             default:
     249           0 :                 elog(ERROR, "unrecognized table_tuple_lock status: %u",
     250             :                      test);
     251             :         }
     252             : 
     253             :         /* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */
     254       10348 :         erm->curCtid = tid;
     255             :     }
     256             : 
     257             :     /*
     258             :      * If we need to do EvalPlanQual testing, do so.
     259             :      */
     260        9852 :     if (epq_needed)
     261             :     {
     262             :         /*
     263             :          * Now fetch any non-locked source rows --- the EPQ logic knows how to
     264             :          * do that.
     265             :          */
     266          38 :         EvalPlanQualSetSlot(&node->lr_epqstate, slot);
     267          38 :         EvalPlanQualFetchRowMarks(&node->lr_epqstate);
     268             : 
     269             :         /*
     270             :          * And finally we can re-evaluate the tuple.
     271             :          */
     272          38 :         slot = EvalPlanQualNext(&node->lr_epqstate);
     273          38 :         if (TupIsNull(slot))
     274             :         {
     275             :             /* Updated tuple fails qual, so ignore it and go on */
     276             :             goto lnext;
     277             :         }
     278             :     }
     279             : 
     280             :     /* Got all locks, so return the current tuple */
     281        9846 :     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        4916 : ExecInitLockRows(LockRows *node, EState *estate, int eflags)
     293             : {
     294             :     LockRowsState *lrstate;
     295        4916 :     Plan       *outerPlan = outerPlan(node);
     296             :     List       *epq_arowmarks;
     297             :     ListCell   *lc;
     298             : 
     299             :     /* check for unsupported flags */
     300             :     Assert(!(eflags & EXEC_FLAG_MARK));
     301             : 
     302             :     /*
     303             :      * create state structure
     304             :      */
     305        4916 :     lrstate = makeNode(LockRowsState);
     306        4916 :     lrstate->ps.plan = (Plan *) node;
     307        4916 :     lrstate->ps.state = estate;
     308        4916 :     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        4916 :     ExecInitResultTypeTL(&lrstate->ps);
     321             : 
     322             :     /*
     323             :      * then initialize outer plan
     324             :      */
     325        4916 :     outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
     326             : 
     327             :     /* node returns unmodified slots from the outer plan */
     328        4916 :     lrstate->ps.resultopsset = true;
     329        4916 :     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        4916 :     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        4916 :     lrstate->lr_arowMarks = NIL;
     344        4916 :     epq_arowmarks = NIL;
     345       10618 :     foreach(lc, node->rowMarks)
     346             :     {
     347        5702 :         PlanRowMark *rc = lfirst_node(PlanRowMark, lc);
     348             :         ExecRowMark *erm;
     349             :         ExecAuxRowMark *aerm;
     350             : 
     351             :         /* ignore "parent" rowmarks; they are irrelevant at runtime */
     352        5702 :         if (rc->isParent)
     353         444 :             continue;
     354             : 
     355             :         /* find ExecRowMark and build ExecAuxRowMark */
     356        5258 :         erm = ExecFindRowMark(estate, rc->rti, false);
     357        5258 :         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        5258 :         if (RowMarkRequiresRowShareLock(erm->markType))
     366        5072 :             lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
     367             :         else
     368         186 :             epq_arowmarks = lappend(epq_arowmarks, aerm);
     369             :     }
     370             : 
     371             :     /* Now we have the info needed to set up EPQ state */
     372        4916 :     EvalPlanQualInit(&lrstate->lr_epqstate, estate,
     373             :                      outerPlan, epq_arowmarks, node->epqParam);
     374             : 
     375        4916 :     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        4840 : ExecEndLockRows(LockRowsState *node)
     387             : {
     388        4840 :     EvalPlanQualEnd(&node->lr_epqstate);
     389        4840 :     ExecEndNode(outerPlanState(node));
     390        4840 : }
     391             : 
     392             : 
     393             : void
     394           0 : ExecReScanLockRows(LockRowsState *node)
     395             : {
     396             :     /*
     397             :      * if chgParam of subnode is not null then plan will be re-scanned by
     398             :      * first ExecProcNode.
     399             :      */
     400           0 :     if (node->ps.lefttree->chgParam == NULL)
     401           0 :         ExecReScan(node->ps.lefttree);
     402           0 : }

Generated by: LCOV version 1.13