1 /*-------------------------------------------------------------------------
4 * Routines to support direct tid scans of relations
6 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.45 2005/11/26 22:14:56 tgl Exp $
13 *-------------------------------------------------------------------------
18 * ExecTidScan scans a relation using tids
19 * ExecInitTidScan creates and initializes state info.
20 * ExecTidReScan rescans the tid relation.
21 * ExecEndTidScan releases all storage.
22 * ExecTidMarkPos marks scan position.
23 * ExecTidRestrPos restores scan position.
27 #include "access/heapam.h"
28 #include "catalog/pg_type.h"
29 #include "executor/execdebug.h"
30 #include "executor/nodeTidscan.h"
31 #include "optimizer/clauses.h"
32 #include "parser/parsetree.h"
33 #include "utils/array.h"
36 #define IsCTIDVar(node) \
39 ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
40 ((Var *) (node))->varlevelsup == 0)
42 static void TidListCreate(TidScanState *tidstate);
43 static int itemptr_comparator(const void *a, const void *b);
44 static TupleTableSlot *TidNext(TidScanState *node);
48 * Compute the list of TIDs to be visited, by evaluating the expressions
51 * (The result is actually an array, not a list.)
54 TidListCreate(TidScanState *tidstate)
56 List *evalList = tidstate->tss_tidquals;
57 ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
58 ItemPointerData *tidList;
64 * We initialize the array with enough slots for the case that all
65 * quals are simple OpExprs. If there's any ScalarArrayOpExprs,
66 * we may have to enlarge the array.
68 numAllocTids = list_length(evalList);
69 tidList = (ItemPointerData *)
70 palloc(numAllocTids * sizeof(ItemPointerData));
75 ExprState *exstate = (ExprState *) lfirst(l);
76 Expr *expr = exstate->expr;
80 if (is_opclause(expr))
82 FuncExprState *fexstate = (FuncExprState *) exstate;
86 arg1 = get_leftop(expr);
87 arg2 = get_rightop(expr);
89 exstate = (ExprState *) lsecond(fexstate->args);
90 else if (IsCTIDVar(arg2))
91 exstate = (ExprState *) linitial(fexstate->args);
93 elog(ERROR, "could not identify CTID variable");
95 itemptr = (ItemPointer)
96 DatumGetPointer(ExecEvalExprSwitchContext(exstate,
100 if (!isNull && ItemPointerIsValid(itemptr))
102 if (numTids >= numAllocTids)
105 tidList = (ItemPointerData *)
107 numAllocTids * sizeof(ItemPointerData));
109 tidList[numTids++] = *itemptr;
112 else if (expr && IsA(expr, ScalarArrayOpExpr))
114 ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
116 ArrayType *itemarray;
122 exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
123 arraydatum = ExecEvalExprSwitchContext(exstate,
129 itemarray = DatumGetArrayTypeP(arraydatum);
130 deconstruct_array(itemarray,
131 TIDOID, SizeOfIptrData, false, 's',
132 &ipdatums, &ipnulls, &ndatums);
133 if (numTids + ndatums > numAllocTids)
135 numAllocTids = numTids + ndatums;
136 tidList = (ItemPointerData *)
138 numAllocTids * sizeof(ItemPointerData));
140 for (i = 0; i < ndatums; i++)
144 itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
145 if (ItemPointerIsValid(itemptr))
146 tidList[numTids++] = *itemptr;
153 elog(ERROR, "could not identify CTID expression");
157 * Sort the array of TIDs into order, and eliminate duplicates.
158 * Eliminating duplicates is necessary since we want OR semantics
159 * across the list. Sorting makes it easier to detect duplicates,
160 * and as a bonus ensures that we will visit the heap in the most
168 qsort((void *) tidList, numTids, sizeof(ItemPointerData),
171 for (i = 1; i < numTids; i++)
173 if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
174 tidList[++lastTid] = tidList[i];
176 numTids = lastTid + 1;
179 tidstate->tss_TidList = tidList;
180 tidstate->tss_NumTids = numTids;
181 tidstate->tss_TidPtr = -1;
185 * qsort comparator for ItemPointerData items
188 itemptr_comparator(const void *a, const void *b)
190 const ItemPointerData *ipa = (const ItemPointerData *) a;
191 const ItemPointerData *ipb = (const ItemPointerData *) b;
192 BlockNumber ba = ItemPointerGetBlockNumber(ipa);
193 BlockNumber bb = ItemPointerGetBlockNumber(ipb);
194 OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
195 OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
208 /* ----------------------------------------------------------------
211 * Retrieve a tuple from the TidScan node's currentRelation
212 * using the tids in the TidScanState information.
214 * ----------------------------------------------------------------
216 static TupleTableSlot *
217 TidNext(TidScanState *node)
220 ScanDirection direction;
222 Relation heapRelation;
224 TupleTableSlot *slot;
226 Buffer buffer = InvalidBuffer;
227 ItemPointerData *tidList;
232 * extract necessary information from tid scan node
234 estate = node->ss.ps.state;
235 direction = estate->es_direction;
236 snapshot = estate->es_snapshot;
237 heapRelation = node->ss.ss_currentRelation;
238 slot = node->ss.ss_ScanTupleSlot;
239 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
242 * Check if we are evaluating PlanQual for tuple of this relation.
243 * Additional checking is not good, but no other way for now. We could
244 * introduce new nodes for this case and handle TidScan --> NewNode
245 * switching in Init/ReScan plan...
247 if (estate->es_evTuple != NULL &&
248 estate->es_evTuple[scanrelid - 1] != NULL)
250 if (estate->es_evTupleNull[scanrelid - 1])
251 return ExecClearTuple(slot);
254 * XXX shouldn't we check here to make sure tuple matches TID list? In
255 * runtime-key case this is not certain, is it?
258 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
259 slot, InvalidBuffer, false);
261 /* Flag for the next call that no more tuples */
262 estate->es_evTupleNull[scanrelid - 1] = true;
267 * First time through, compute the list of TIDs to be visited
269 if (node->tss_TidList == NULL)
272 tidList = node->tss_TidList;
273 numTids = node->tss_NumTids;
275 tuple = &(node->tss_htup);
278 * Initialize or advance scan position, depending on direction.
280 bBackward = ScanDirectionIsBackward(direction);
283 if (node->tss_TidPtr < 0)
285 /* initialize for backward scan */
286 node->tss_TidPtr = numTids - 1;
293 if (node->tss_TidPtr < 0)
295 /* initialize for forward scan */
296 node->tss_TidPtr = 0;
302 while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
304 tuple->t_self = tidList[node->tss_TidPtr];
305 if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
308 * store the scanned tuple in the scan tuple slot of the scan
309 * state. Eventually we will only do this and not return a tuple.
310 * Note: we pass 'false' because tuples returned by amgetnext are
311 * pointers onto disk pages and were not created with palloc() and
312 * so should not be pfree()'d.
314 ExecStoreTuple(tuple, /* tuple to store */
315 slot, /* slot to store in */
316 buffer, /* buffer associated with tuple */
317 false); /* don't pfree */
320 * At this point we have an extra pin on the buffer, because
321 * ExecStoreTuple incremented the pin count. Drop our local pin.
323 ReleaseBuffer(buffer);
327 /* Bad TID or failed snapshot qual; try next */
335 * if we get here it means the tid scan failed so we are at the end of the
338 return ExecClearTuple(slot);
341 /* ----------------------------------------------------------------
344 * Scans the relation using tids and returns
345 * the next qualifying tuple in the direction specified.
346 * It calls ExecScan() and passes it the access methods which returns
347 * the next tuple using the tids.
350 * -- the "cursor" maintained by the AMI is positioned at the tuple
351 * returned previously.
354 * -- the relation indicated is opened for scanning so that the
355 * "cursor" is positioned before the first qualifying tuple.
357 * ----------------------------------------------------------------
360 ExecTidScan(TidScanState *node)
363 * use TidNext as access method
365 return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
368 /* ----------------------------------------------------------------
369 * ExecTidReScan(node)
370 * ----------------------------------------------------------------
373 ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
378 estate = node->ss.ps.state;
379 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
381 /* If we are being passed an outer tuple, save it for runtime key calc */
382 if (exprCtxt != NULL)
383 node->ss.ps.ps_ExprContext->ecxt_outertuple =
384 exprCtxt->ecxt_outertuple;
386 /* If this is re-scanning of PlanQual ... */
387 if (estate->es_evTuple != NULL &&
388 estate->es_evTuple[scanrelid - 1] != NULL)
390 estate->es_evTupleNull[scanrelid - 1] = false;
394 if (node->tss_TidList)
395 pfree(node->tss_TidList);
396 node->tss_TidList = NULL;
397 node->tss_NumTids = 0;
398 node->tss_TidPtr = -1;
401 /* ----------------------------------------------------------------
404 * Releases any storage allocated through C routines.
406 * ----------------------------------------------------------------
409 ExecEndTidScan(TidScanState *node)
412 * Free the exprcontext
414 ExecFreeExprContext(&node->ss.ps);
417 * clear out tuple table slots
419 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
420 ExecClearTuple(node->ss.ss_ScanTupleSlot);
423 * close the heap relation.
425 * Currently, we do not release the AccessShareLock acquired by
426 * ExecInitTidScan. This lock should be held till end of transaction.
427 * (There is a faction that considers this too much locking, however.)
429 heap_close(node->ss.ss_currentRelation, NoLock);
432 /* ----------------------------------------------------------------
435 * Marks scan position by marking the current tid.
437 * ----------------------------------------------------------------
440 ExecTidMarkPos(TidScanState *node)
442 node->tss_MarkTidPtr = node->tss_TidPtr;
445 /* ----------------------------------------------------------------
448 * Restores scan position by restoring the current tid.
451 * XXX Assumes previously marked scan position belongs to current tid
452 * ----------------------------------------------------------------
455 ExecTidRestrPos(TidScanState *node)
457 node->tss_TidPtr = node->tss_MarkTidPtr;
460 /* ----------------------------------------------------------------
463 * Initializes the tid scan's state information, creates
464 * scan keys, and opens the base and tid relations.
467 * node: TidNode node produced by the planner.
468 * estate: the execution state initialized in InitPlan.
469 * ----------------------------------------------------------------
472 ExecInitTidScan(TidScan *node, EState *estate)
474 TidScanState *tidstate;
475 RangeTblEntry *rtentry;
478 Relation currentRelation;
481 * create state structure
483 tidstate = makeNode(TidScanState);
484 tidstate->ss.ps.plan = (Plan *) node;
485 tidstate->ss.ps.state = estate;
488 * Miscellaneous initialization
490 * create expression context for node
492 ExecAssignExprContext(estate, &tidstate->ss.ps);
495 * initialize child expressions
497 tidstate->ss.ps.targetlist = (List *)
498 ExecInitExpr((Expr *) node->scan.plan.targetlist,
499 (PlanState *) tidstate);
500 tidstate->ss.ps.qual = (List *)
501 ExecInitExpr((Expr *) node->scan.plan.qual,
502 (PlanState *) tidstate);
504 tidstate->tss_tidquals = (List *)
505 ExecInitExpr((Expr *) node->tidquals,
506 (PlanState *) tidstate);
508 #define TIDSCAN_NSLOTS 2
511 * tuple table initialization
513 ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
514 ExecInitScanTupleSlot(estate, &tidstate->ss);
517 * mark tid list as not computed yet
519 tidstate->tss_TidList = NULL;
520 tidstate->tss_NumTids = 0;
521 tidstate->tss_TidPtr = -1;
524 * open the base relation
526 * We acquire AccessShareLock for the duration of the scan.
528 relid = node->scan.scanrelid;
529 rtentry = rt_fetch(relid, estate->es_range_table);
530 reloid = rtentry->relid;
532 currentRelation = heap_open(reloid, AccessShareLock);
534 tidstate->ss.ss_currentRelation = currentRelation;
535 tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
538 * get the scan type from the relation descriptor.
540 ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
543 * Initialize result tuple type and projection info.
545 ExecAssignResultTypeFromTL(&tidstate->ss.ps);
546 ExecAssignScanProjectionInfo(&tidstate->ss);
555 ExecCountSlotsTidScan(TidScan *node)
557 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
558 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;