Header And Logo

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

nodeTidscan.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * nodeTidscan.c
00004  *    Routines to support direct tid scans of relations
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/nodeTidscan.c
00012  *
00013  *-------------------------------------------------------------------------
00014  */
00015 /*
00016  * INTERFACE ROUTINES
00017  *
00018  *      ExecTidScan         scans a relation using tids
00019  *      ExecInitTidScan     creates and initializes state info.
00020  *      ExecReScanTidScan   rescans the tid relation.
00021  *      ExecEndTidScan      releases all storage.
00022  *      ExecTidMarkPos      marks scan position.
00023  *      ExecTidRestrPos     restores scan position.
00024  */
00025 #include "postgres.h"
00026 
00027 #include "access/sysattr.h"
00028 #include "catalog/pg_type.h"
00029 #include "executor/execdebug.h"
00030 #include "executor/nodeTidscan.h"
00031 #include "optimizer/clauses.h"
00032 #include "storage/bufmgr.h"
00033 #include "utils/array.h"
00034 #include "utils/rel.h"
00035 
00036 
00037 #define IsCTIDVar(node)  \
00038     ((node) != NULL && \
00039      IsA((node), Var) && \
00040      ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
00041      ((Var *) (node))->varlevelsup == 0)
00042 
00043 static void TidListCreate(TidScanState *tidstate);
00044 static int  itemptr_comparator(const void *a, const void *b);
00045 static TupleTableSlot *TidNext(TidScanState *node);
00046 
00047 
00048 /*
00049  * Compute the list of TIDs to be visited, by evaluating the expressions
00050  * for them.
00051  *
00052  * (The result is actually an array, not a list.)
00053  */
00054 static void
00055 TidListCreate(TidScanState *tidstate)
00056 {
00057     List       *evalList = tidstate->tss_tidquals;
00058     ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
00059     BlockNumber nblocks;
00060     ItemPointerData *tidList;
00061     int         numAllocTids;
00062     int         numTids;
00063     ListCell   *l;
00064 
00065     /*
00066      * We silently discard any TIDs that are out of range at the time of scan
00067      * start.  (Since we hold at least AccessShareLock on the table, it won't
00068      * be possible for someone to truncate away the blocks we intend to
00069      * visit.)
00070      */
00071     nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation);
00072 
00073     /*
00074      * We initialize the array with enough slots for the case that all quals
00075      * are simple OpExprs or CurrentOfExprs.  If there are any
00076      * ScalarArrayOpExprs, we may have to enlarge the array.
00077      */
00078     numAllocTids = list_length(evalList);
00079     tidList = (ItemPointerData *)
00080         palloc(numAllocTids * sizeof(ItemPointerData));
00081     numTids = 0;
00082     tidstate->tss_isCurrentOf = false;
00083 
00084     foreach(l, evalList)
00085     {
00086         ExprState  *exstate = (ExprState *) lfirst(l);
00087         Expr       *expr = exstate->expr;
00088         ItemPointer itemptr;
00089         bool        isNull;
00090 
00091         if (is_opclause(expr))
00092         {
00093             FuncExprState *fexstate = (FuncExprState *) exstate;
00094             Node       *arg1;
00095             Node       *arg2;
00096 
00097             arg1 = get_leftop(expr);
00098             arg2 = get_rightop(expr);
00099             if (IsCTIDVar(arg1))
00100                 exstate = (ExprState *) lsecond(fexstate->args);
00101             else if (IsCTIDVar(arg2))
00102                 exstate = (ExprState *) linitial(fexstate->args);
00103             else
00104                 elog(ERROR, "could not identify CTID variable");
00105 
00106             itemptr = (ItemPointer)
00107                 DatumGetPointer(ExecEvalExprSwitchContext(exstate,
00108                                                           econtext,
00109                                                           &isNull,
00110                                                           NULL));
00111             if (!isNull &&
00112                 ItemPointerIsValid(itemptr) &&
00113                 ItemPointerGetBlockNumber(itemptr) < nblocks)
00114             {
00115                 if (numTids >= numAllocTids)
00116                 {
00117                     numAllocTids *= 2;
00118                     tidList = (ItemPointerData *)
00119                         repalloc(tidList,
00120                                  numAllocTids * sizeof(ItemPointerData));
00121                 }
00122                 tidList[numTids++] = *itemptr;
00123             }
00124         }
00125         else if (expr && IsA(expr, ScalarArrayOpExpr))
00126         {
00127             ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
00128             Datum       arraydatum;
00129             ArrayType  *itemarray;
00130             Datum      *ipdatums;
00131             bool       *ipnulls;
00132             int         ndatums;
00133             int         i;
00134 
00135             exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
00136             arraydatum = ExecEvalExprSwitchContext(exstate,
00137                                                    econtext,
00138                                                    &isNull,
00139                                                    NULL);
00140             if (isNull)
00141                 continue;
00142             itemarray = DatumGetArrayTypeP(arraydatum);
00143             deconstruct_array(itemarray,
00144                               TIDOID, SizeOfIptrData, false, 's',
00145                               &ipdatums, &ipnulls, &ndatums);
00146             if (numTids + ndatums > numAllocTids)
00147             {
00148                 numAllocTids = numTids + ndatums;
00149                 tidList = (ItemPointerData *)
00150                     repalloc(tidList,
00151                              numAllocTids * sizeof(ItemPointerData));
00152             }
00153             for (i = 0; i < ndatums; i++)
00154             {
00155                 if (!ipnulls[i])
00156                 {
00157                     itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
00158                     if (ItemPointerIsValid(itemptr) &&
00159                         ItemPointerGetBlockNumber(itemptr) < nblocks)
00160                         tidList[numTids++] = *itemptr;
00161                 }
00162             }
00163             pfree(ipdatums);
00164             pfree(ipnulls);
00165         }
00166         else if (expr && IsA(expr, CurrentOfExpr))
00167         {
00168             CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
00169             ItemPointerData cursor_tid;
00170 
00171             if (execCurrentOf(cexpr, econtext,
00172                            RelationGetRelid(tidstate->ss.ss_currentRelation),
00173                               &cursor_tid))
00174             {
00175                 if (numTids >= numAllocTids)
00176                 {
00177                     numAllocTids *= 2;
00178                     tidList = (ItemPointerData *)
00179                         repalloc(tidList,
00180                                  numAllocTids * sizeof(ItemPointerData));
00181                 }
00182                 tidList[numTids++] = cursor_tid;
00183                 tidstate->tss_isCurrentOf = true;
00184             }
00185         }
00186         else
00187             elog(ERROR, "could not identify CTID expression");
00188     }
00189 
00190     /*
00191      * Sort the array of TIDs into order, and eliminate duplicates.
00192      * Eliminating duplicates is necessary since we want OR semantics across
00193      * the list.  Sorting makes it easier to detect duplicates, and as a bonus
00194      * ensures that we will visit the heap in the most efficient way.
00195      */
00196     if (numTids > 1)
00197     {
00198         int         lastTid;
00199         int         i;
00200 
00201         /* CurrentOfExpr could never appear OR'd with something else */
00202         Assert(!tidstate->tss_isCurrentOf);
00203 
00204         qsort((void *) tidList, numTids, sizeof(ItemPointerData),
00205               itemptr_comparator);
00206         lastTid = 0;
00207         for (i = 1; i < numTids; i++)
00208         {
00209             if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
00210                 tidList[++lastTid] = tidList[i];
00211         }
00212         numTids = lastTid + 1;
00213     }
00214 
00215     tidstate->tss_TidList = tidList;
00216     tidstate->tss_NumTids = numTids;
00217     tidstate->tss_TidPtr = -1;
00218 }
00219 
00220 /*
00221  * qsort comparator for ItemPointerData items
00222  */
00223 static int
00224 itemptr_comparator(const void *a, const void *b)
00225 {
00226     const ItemPointerData *ipa = (const ItemPointerData *) a;
00227     const ItemPointerData *ipb = (const ItemPointerData *) b;
00228     BlockNumber ba = ItemPointerGetBlockNumber(ipa);
00229     BlockNumber bb = ItemPointerGetBlockNumber(ipb);
00230     OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
00231     OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
00232 
00233     if (ba < bb)
00234         return -1;
00235     if (ba > bb)
00236         return 1;
00237     if (oa < ob)
00238         return -1;
00239     if (oa > ob)
00240         return 1;
00241     return 0;
00242 }
00243 
00244 /* ----------------------------------------------------------------
00245  *      TidNext
00246  *
00247  *      Retrieve a tuple from the TidScan node's currentRelation
00248  *      using the tids in the TidScanState information.
00249  *
00250  * ----------------------------------------------------------------
00251  */
00252 static TupleTableSlot *
00253 TidNext(TidScanState *node)
00254 {
00255     EState     *estate;
00256     ScanDirection direction;
00257     Snapshot    snapshot;
00258     Relation    heapRelation;
00259     HeapTuple   tuple;
00260     TupleTableSlot *slot;
00261     Buffer      buffer = InvalidBuffer;
00262     ItemPointerData *tidList;
00263     int         numTids;
00264     bool        bBackward;
00265 
00266     /*
00267      * extract necessary information from tid scan node
00268      */
00269     estate = node->ss.ps.state;
00270     direction = estate->es_direction;
00271     snapshot = estate->es_snapshot;
00272     heapRelation = node->ss.ss_currentRelation;
00273     slot = node->ss.ss_ScanTupleSlot;
00274 
00275     /*
00276      * First time through, compute the list of TIDs to be visited
00277      */
00278     if (node->tss_TidList == NULL)
00279         TidListCreate(node);
00280 
00281     tidList = node->tss_TidList;
00282     numTids = node->tss_NumTids;
00283 
00284     tuple = &(node->tss_htup);
00285 
00286     /*
00287      * Initialize or advance scan position, depending on direction.
00288      */
00289     bBackward = ScanDirectionIsBackward(direction);
00290     if (bBackward)
00291     {
00292         if (node->tss_TidPtr < 0)
00293         {
00294             /* initialize for backward scan */
00295             node->tss_TidPtr = numTids - 1;
00296         }
00297         else
00298             node->tss_TidPtr--;
00299     }
00300     else
00301     {
00302         if (node->tss_TidPtr < 0)
00303         {
00304             /* initialize for forward scan */
00305             node->tss_TidPtr = 0;
00306         }
00307         else
00308             node->tss_TidPtr++;
00309     }
00310 
00311     while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
00312     {
00313         tuple->t_self = tidList[node->tss_TidPtr];
00314 
00315         /*
00316          * For WHERE CURRENT OF, the tuple retrieved from the cursor might
00317          * since have been updated; if so, we should fetch the version that is
00318          * current according to our snapshot.
00319          */
00320         if (node->tss_isCurrentOf)
00321             heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
00322 
00323         if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
00324         {
00325             /*
00326              * store the scanned tuple in the scan tuple slot of the scan
00327              * state.  Eventually we will only do this and not return a tuple.
00328              * Note: we pass 'false' because tuples returned by amgetnext are
00329              * pointers onto disk pages and were not created with palloc() and
00330              * so should not be pfree()'d.
00331              */
00332             ExecStoreTuple(tuple,       /* tuple to store */
00333                            slot,    /* slot to store in */
00334                            buffer,      /* buffer associated with tuple  */
00335                            false);      /* don't pfree */
00336 
00337             /*
00338              * At this point we have an extra pin on the buffer, because
00339              * ExecStoreTuple incremented the pin count. Drop our local pin.
00340              */
00341             ReleaseBuffer(buffer);
00342 
00343             return slot;
00344         }
00345         /* Bad TID or failed snapshot qual; try next */
00346         if (bBackward)
00347             node->tss_TidPtr--;
00348         else
00349             node->tss_TidPtr++;
00350     }
00351 
00352     /*
00353      * if we get here it means the tid scan failed so we are at the end of the
00354      * scan..
00355      */
00356     return ExecClearTuple(slot);
00357 }
00358 
00359 /*
00360  * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
00361  */
00362 static bool
00363 TidRecheck(TidScanState *node, TupleTableSlot *slot)
00364 {
00365     /*
00366      * XXX shouldn't we check here to make sure tuple matches TID list? In
00367      * runtime-key case this is not certain, is it?  However, in the WHERE
00368      * CURRENT OF case it might not match anyway ...
00369      */
00370     return true;
00371 }
00372 
00373 
00374 /* ----------------------------------------------------------------
00375  *      ExecTidScan(node)
00376  *
00377  *      Scans the relation using tids and returns
00378  *         the next qualifying tuple in the direction specified.
00379  *      We call the ExecScan() routine and pass it the appropriate
00380  *      access method functions.
00381  *
00382  *      Conditions:
00383  *        -- the "cursor" maintained by the AMI is positioned at the tuple
00384  *           returned previously.
00385  *
00386  *      Initial States:
00387  *        -- the relation indicated is opened for scanning so that the
00388  *           "cursor" is positioned before the first qualifying tuple.
00389  *        -- tidPtr is -1.
00390  * ----------------------------------------------------------------
00391  */
00392 TupleTableSlot *
00393 ExecTidScan(TidScanState *node)
00394 {
00395     return ExecScan(&node->ss,
00396                     (ExecScanAccessMtd) TidNext,
00397                     (ExecScanRecheckMtd) TidRecheck);
00398 }
00399 
00400 /* ----------------------------------------------------------------
00401  *      ExecReScanTidScan(node)
00402  * ----------------------------------------------------------------
00403  */
00404 void
00405 ExecReScanTidScan(TidScanState *node)
00406 {
00407     if (node->tss_TidList)
00408         pfree(node->tss_TidList);
00409     node->tss_TidList = NULL;
00410     node->tss_NumTids = 0;
00411     node->tss_TidPtr = -1;
00412 
00413     ExecScanReScan(&node->ss);
00414 }
00415 
00416 /* ----------------------------------------------------------------
00417  *      ExecEndTidScan
00418  *
00419  *      Releases any storage allocated through C routines.
00420  *      Returns nothing.
00421  * ----------------------------------------------------------------
00422  */
00423 void
00424 ExecEndTidScan(TidScanState *node)
00425 {
00426     /*
00427      * Free the exprcontext
00428      */
00429     ExecFreeExprContext(&node->ss.ps);
00430 
00431     /*
00432      * clear out tuple table slots
00433      */
00434     ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
00435     ExecClearTuple(node->ss.ss_ScanTupleSlot);
00436 
00437     /*
00438      * close the heap relation.
00439      */
00440     ExecCloseScanRelation(node->ss.ss_currentRelation);
00441 }
00442 
00443 /* ----------------------------------------------------------------
00444  *      ExecTidMarkPos
00445  *
00446  *      Marks scan position by marking the current tid.
00447  *      Returns nothing.
00448  * ----------------------------------------------------------------
00449  */
00450 void
00451 ExecTidMarkPos(TidScanState *node)
00452 {
00453     node->tss_MarkTidPtr = node->tss_TidPtr;
00454 }
00455 
00456 /* ----------------------------------------------------------------
00457  *      ExecTidRestrPos
00458  *
00459  *      Restores scan position by restoring the current tid.
00460  *      Returns nothing.
00461  *
00462  *      XXX Assumes previously marked scan position belongs to current tid
00463  * ----------------------------------------------------------------
00464  */
00465 void
00466 ExecTidRestrPos(TidScanState *node)
00467 {
00468     node->tss_TidPtr = node->tss_MarkTidPtr;
00469 }
00470 
00471 /* ----------------------------------------------------------------
00472  *      ExecInitTidScan
00473  *
00474  *      Initializes the tid scan's state information, creates
00475  *      scan keys, and opens the base and tid relations.
00476  *
00477  *      Parameters:
00478  *        node: TidNode node produced by the planner.
00479  *        estate: the execution state initialized in InitPlan.
00480  * ----------------------------------------------------------------
00481  */
00482 TidScanState *
00483 ExecInitTidScan(TidScan *node, EState *estate, int eflags)
00484 {
00485     TidScanState *tidstate;
00486     Relation    currentRelation;
00487 
00488     /*
00489      * create state structure
00490      */
00491     tidstate = makeNode(TidScanState);
00492     tidstate->ss.ps.plan = (Plan *) node;
00493     tidstate->ss.ps.state = estate;
00494 
00495     /*
00496      * Miscellaneous initialization
00497      *
00498      * create expression context for node
00499      */
00500     ExecAssignExprContext(estate, &tidstate->ss.ps);
00501 
00502     tidstate->ss.ps.ps_TupFromTlist = false;
00503 
00504     /*
00505      * initialize child expressions
00506      */
00507     tidstate->ss.ps.targetlist = (List *)
00508         ExecInitExpr((Expr *) node->scan.plan.targetlist,
00509                      (PlanState *) tidstate);
00510     tidstate->ss.ps.qual = (List *)
00511         ExecInitExpr((Expr *) node->scan.plan.qual,
00512                      (PlanState *) tidstate);
00513 
00514     tidstate->tss_tidquals = (List *)
00515         ExecInitExpr((Expr *) node->tidquals,
00516                      (PlanState *) tidstate);
00517 
00518     /*
00519      * tuple table initialization
00520      */
00521     ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
00522     ExecInitScanTupleSlot(estate, &tidstate->ss);
00523 
00524     /*
00525      * mark tid list as not computed yet
00526      */
00527     tidstate->tss_TidList = NULL;
00528     tidstate->tss_NumTids = 0;
00529     tidstate->tss_TidPtr = -1;
00530 
00531     /*
00532      * open the base relation and acquire appropriate lock on it.
00533      */
00534     currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
00535 
00536     tidstate->ss.ss_currentRelation = currentRelation;
00537     tidstate->ss.ss_currentScanDesc = NULL;     /* no heap scan here */
00538 
00539     /*
00540      * get the scan type from the relation descriptor.
00541      */
00542     ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation));
00543 
00544     /*
00545      * Initialize result tuple type and projection info.
00546      */
00547     ExecAssignResultTypeFromTL(&tidstate->ss.ps);
00548     ExecAssignScanProjectionInfo(&tidstate->ss);
00549 
00550     /*
00551      * all done.
00552      */
00553     return tidstate;
00554 }