Header And Logo

PostgreSQL
| The world's most advanced open source database.

nodeLockRows.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * nodeLockRows.c
00004  *    Routines to handle FOR UPDATE/FOR SHARE row locking
00005  *
00006  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00007  * Portions Copyright (c) 1994, Regents of the University of California
00008  *
00009  *
00010  * IDENTIFICATION
00011  *    src/backend/executor/nodeLockRows.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 /*
00016  * INTERFACE ROUTINES
00017  *      ExecLockRows        - fetch locked rows
00018  *      ExecInitLockRows    - initialize node and subnodes..
00019  *      ExecEndLockRows     - shutdown node and subnodes
00020  */
00021 
00022 #include "postgres.h"
00023 
00024 #include "access/htup_details.h"
00025 #include "access/xact.h"
00026 #include "executor/executor.h"
00027 #include "executor/nodeLockRows.h"
00028 #include "storage/bufmgr.h"
00029 #include "utils/rel.h"
00030 #include "utils/tqual.h"
00031 
00032 
00033 /* ----------------------------------------------------------------
00034  *      ExecLockRows
00035  * ----------------------------------------------------------------
00036  */
00037 TupleTableSlot *                /* return: a tuple or NULL */
00038 ExecLockRows(LockRowsState *node)
00039 {
00040     TupleTableSlot *slot;
00041     EState     *estate;
00042     PlanState  *outerPlan;
00043     bool        epq_started;
00044     ListCell   *lc;
00045 
00046     /*
00047      * get information from the node
00048      */
00049     estate = node->ps.state;
00050     outerPlan = outerPlanState(node);
00051 
00052     /*
00053      * Get next tuple from subplan, if any.
00054      */
00055 lnext:
00056     slot = ExecProcNode(outerPlan);
00057 
00058     if (TupIsNull(slot))
00059         return NULL;
00060 
00061     /*
00062      * Attempt to lock the source tuple(s).  (Note we only have locking
00063      * rowmarks in lr_arowMarks.)
00064      */
00065     epq_started = false;
00066     foreach(lc, node->lr_arowMarks)
00067     {
00068         ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
00069         ExecRowMark *erm = aerm->rowmark;
00070         Datum       datum;
00071         bool        isNull;
00072         HeapTupleData tuple;
00073         Buffer      buffer;
00074         HeapUpdateFailureData hufd;
00075         LockTupleMode lockmode;
00076         HTSU_Result test;
00077         HeapTuple   copyTuple;
00078 
00079         /* clear any leftover test tuple for this rel */
00080         if (node->lr_epqstate.estate != NULL)
00081             EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, NULL);
00082 
00083         /* if child rel, must check whether it produced this row */
00084         if (erm->rti != erm->prti)
00085         {
00086             Oid         tableoid;
00087 
00088             datum = ExecGetJunkAttribute(slot,
00089                                          aerm->toidAttNo,
00090                                          &isNull);
00091             /* shouldn't ever get a null result... */
00092             if (isNull)
00093                 elog(ERROR, "tableoid is NULL");
00094             tableoid = DatumGetObjectId(datum);
00095 
00096             if (tableoid != RelationGetRelid(erm->relation))
00097             {
00098                 /* this child is inactive right now */
00099                 ItemPointerSetInvalid(&(erm->curCtid));
00100                 continue;
00101             }
00102         }
00103 
00104         /* fetch the tuple's ctid */
00105         datum = ExecGetJunkAttribute(slot,
00106                                      aerm->ctidAttNo,
00107                                      &isNull);
00108         /* shouldn't ever get a null result... */
00109         if (isNull)
00110             elog(ERROR, "ctid is NULL");
00111         tuple.t_self = *((ItemPointer) DatumGetPointer(datum));
00112 
00113         /* okay, try to lock the tuple */
00114         switch (erm->markType)
00115         {
00116             case ROW_MARK_EXCLUSIVE:
00117                 lockmode = LockTupleExclusive;
00118                 break;
00119             case ROW_MARK_NOKEYEXCLUSIVE:
00120                 lockmode = LockTupleNoKeyExclusive;
00121                 break;
00122             case ROW_MARK_SHARE:
00123                 lockmode = LockTupleShare;
00124                 break;
00125             case ROW_MARK_KEYSHARE:
00126                 lockmode = LockTupleKeyShare;
00127                 break;
00128             default:
00129                 elog(ERROR, "unsupported rowmark type");
00130                 lockmode = LockTupleNoKeyExclusive; /* keep compiler quiet */
00131                 break;
00132         }
00133 
00134         test = heap_lock_tuple(erm->relation, &tuple,
00135                                estate->es_output_cid,
00136                                lockmode, erm->noWait, true,
00137                                &buffer, &hufd);
00138         ReleaseBuffer(buffer);
00139         switch (test)
00140         {
00141             case HeapTupleSelfUpdated:
00142                 /*
00143                  * The target tuple was already updated or deleted by the
00144                  * current command, or by a later command in the current
00145                  * transaction.  We *must* ignore the tuple in the former
00146                  * case, so as to avoid the "Halloween problem" of repeated
00147                  * update attempts.  In the latter case it might be sensible
00148                  * to fetch the updated tuple instead, but doing so would
00149                  * require changing heap_lock_tuple as well as heap_update and
00150                  * heap_delete to not complain about updating "invisible"
00151                  * tuples, which seems pretty scary.  So for now, treat the
00152                  * tuple as deleted and do not process.
00153                  */
00154                 goto lnext;
00155 
00156             case HeapTupleMayBeUpdated:
00157                 /* got the lock successfully */
00158                 break;
00159 
00160             case HeapTupleUpdated:
00161                 if (IsolationUsesXactSnapshot())
00162                     ereport(ERROR,
00163                             (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
00164                              errmsg("could not serialize access due to concurrent update")));
00165                 if (ItemPointerEquals(&hufd.ctid, &tuple.t_self))
00166                 {
00167                     /* Tuple was deleted, so don't return it */
00168                     goto lnext;
00169                 }
00170 
00171                 /* updated, so fetch and lock the updated version */
00172                 copyTuple = EvalPlanQualFetch(estate, erm->relation, lockmode,
00173                                               &hufd.ctid, hufd.xmax);
00174 
00175                 if (copyTuple == NULL)
00176                 {
00177                     /* Tuple was deleted, so don't return it */
00178                     goto lnext;
00179                 }
00180                 /* remember the actually locked tuple's TID */
00181                 tuple.t_self = copyTuple->t_self;
00182 
00183                 /*
00184                  * Need to run a recheck subquery.  Initialize EPQ state if we
00185                  * didn't do so already.
00186                  */
00187                 if (!epq_started)
00188                 {
00189                     EvalPlanQualBegin(&node->lr_epqstate, estate);
00190                     epq_started = true;
00191                 }
00192 
00193                 /* Store target tuple for relation's scan node */
00194                 EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, copyTuple);
00195 
00196                 /* Continue loop until we have all target tuples */
00197                 break;
00198 
00199             default:
00200                 elog(ERROR, "unrecognized heap_lock_tuple status: %u",
00201                      test);
00202         }
00203 
00204         /* Remember locked tuple's TID for WHERE CURRENT OF */
00205         erm->curCtid = tuple.t_self;
00206     }
00207 
00208     /*
00209      * If we need to do EvalPlanQual testing, do so.
00210      */
00211     if (epq_started)
00212     {
00213         /*
00214          * First, fetch a copy of any rows that were successfully locked
00215          * without any update having occurred.  (We do this in a separate pass
00216          * so as to avoid overhead in the common case where there are no
00217          * concurrent updates.)
00218          */
00219         foreach(lc, node->lr_arowMarks)
00220         {
00221             ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc);
00222             ExecRowMark *erm = aerm->rowmark;
00223             HeapTupleData tuple;
00224             Buffer      buffer;
00225 
00226             /* ignore non-active child tables */
00227             if (!ItemPointerIsValid(&(erm->curCtid)))
00228             {
00229                 Assert(erm->rti != erm->prti);  /* check it's child table */
00230                 continue;
00231             }
00232 
00233             if (EvalPlanQualGetTuple(&node->lr_epqstate, erm->rti) != NULL)
00234                 continue;       /* it was updated and fetched above */
00235 
00236             /* okay, fetch the tuple */
00237             tuple.t_self = erm->curCtid;
00238             if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer,
00239                             false, NULL))
00240                 elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck");
00241 
00242             /* successful, copy and store tuple */
00243             EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti,
00244                                  heap_copytuple(&tuple));
00245             ReleaseBuffer(buffer);
00246         }
00247 
00248         /*
00249          * Now fetch any non-locked source rows --- the EPQ logic knows how to
00250          * do that.
00251          */
00252         EvalPlanQualSetSlot(&node->lr_epqstate, slot);
00253         EvalPlanQualFetchRowMarks(&node->lr_epqstate);
00254 
00255         /*
00256          * And finally we can re-evaluate the tuple.
00257          */
00258         slot = EvalPlanQualNext(&node->lr_epqstate);
00259         if (TupIsNull(slot))
00260         {
00261             /* Updated tuple fails qual, so ignore it and go on */
00262             goto lnext;
00263         }
00264     }
00265 
00266     /* Got all locks, so return the current tuple */
00267     return slot;
00268 }
00269 
00270 /* ----------------------------------------------------------------
00271  *      ExecInitLockRows
00272  *
00273  *      This initializes the LockRows node state structures and
00274  *      the node's subplan.
00275  * ----------------------------------------------------------------
00276  */
00277 LockRowsState *
00278 ExecInitLockRows(LockRows *node, EState *estate, int eflags)
00279 {
00280     LockRowsState *lrstate;
00281     Plan       *outerPlan = outerPlan(node);
00282     List       *epq_arowmarks;
00283     ListCell   *lc;
00284 
00285     /* check for unsupported flags */
00286     Assert(!(eflags & EXEC_FLAG_MARK));
00287 
00288     /*
00289      * create state structure
00290      */
00291     lrstate = makeNode(LockRowsState);
00292     lrstate->ps.plan = (Plan *) node;
00293     lrstate->ps.state = estate;
00294 
00295     /*
00296      * Miscellaneous initialization
00297      *
00298      * LockRows nodes never call ExecQual or ExecProject.
00299      */
00300 
00301     /*
00302      * Tuple table initialization (XXX not actually used...)
00303      */
00304     ExecInitResultTupleSlot(estate, &lrstate->ps);
00305 
00306     /*
00307      * then initialize outer plan
00308      */
00309     outerPlanState(lrstate) = ExecInitNode(outerPlan, estate, eflags);
00310 
00311     /*
00312      * LockRows nodes do no projections, so initialize projection info for
00313      * this node appropriately
00314      */
00315     ExecAssignResultTypeFromTL(&lrstate->ps);
00316     lrstate->ps.ps_ProjInfo = NULL;
00317 
00318     /*
00319      * Locate the ExecRowMark(s) that this node is responsible for, and
00320      * construct ExecAuxRowMarks for them.  (InitPlan should already have
00321      * built the global list of ExecRowMarks.)
00322      */
00323     lrstate->lr_arowMarks = NIL;
00324     epq_arowmarks = NIL;
00325     foreach(lc, node->rowMarks)
00326     {
00327         PlanRowMark *rc = (PlanRowMark *) lfirst(lc);
00328         ExecRowMark *erm;
00329         ExecAuxRowMark *aerm;
00330 
00331         Assert(IsA(rc, PlanRowMark));
00332 
00333         /* ignore "parent" rowmarks; they are irrelevant at runtime */
00334         if (rc->isParent)
00335             continue;
00336 
00337         /* find ExecRowMark and build ExecAuxRowMark */
00338         erm = ExecFindRowMark(estate, rc->rti);
00339         aerm = ExecBuildAuxRowMark(erm, outerPlan->targetlist);
00340 
00341         /*
00342          * Only locking rowmarks go into our own list.  Non-locking marks are
00343          * passed off to the EvalPlanQual machinery.  This is because we don't
00344          * want to bother fetching non-locked rows unless we actually have to
00345          * do an EPQ recheck.
00346          */
00347         if (RowMarkRequiresRowShareLock(erm->markType))
00348             lrstate->lr_arowMarks = lappend(lrstate->lr_arowMarks, aerm);
00349         else
00350             epq_arowmarks = lappend(epq_arowmarks, aerm);
00351     }
00352 
00353     /* Now we have the info needed to set up EPQ state */
00354     EvalPlanQualInit(&lrstate->lr_epqstate, estate,
00355                      outerPlan, epq_arowmarks, node->epqParam);
00356 
00357     return lrstate;
00358 }
00359 
00360 /* ----------------------------------------------------------------
00361  *      ExecEndLockRows
00362  *
00363  *      This shuts down the subplan and frees resources allocated
00364  *      to this node.
00365  * ----------------------------------------------------------------
00366  */
00367 void
00368 ExecEndLockRows(LockRowsState *node)
00369 {
00370     EvalPlanQualEnd(&node->lr_epqstate);
00371     ExecEndNode(outerPlanState(node));
00372 }
00373 
00374 
00375 void
00376 ExecReScanLockRows(LockRowsState *node)
00377 {
00378     /*
00379      * if chgParam of subnode is not null then plan will be re-scanned by
00380      * first ExecProcNode.
00381      */
00382     if (node->ps.lefttree->chgParam == NULL)
00383         ExecReScan(node->ps.lefttree);
00384 }