1 /*-------------------------------------------------------------------------
4 * Routines to support direct tid scans of relations
6 * Portions Copyright (c) 1996-2003, 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.39 2004/05/30 23:40:26 neilc 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 "executor/execdebug.h"
28 #include "executor/nodeTidscan.h"
29 #include "access/heapam.h"
30 #include "parser/parsetree.h"
33 static void TidListCreate(TidScanState *tidstate);
34 static TupleTableSlot *TidNext(TidScanState *node);
38 * Compute the list of TIDs to be visited, by evaluating the expressions
42 TidListCreate(TidScanState *tidstate)
44 List *evalList = tidstate->tss_tideval;
45 ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
46 ItemPointerData *tidList;
50 tidList = (ItemPointerData *)
51 palloc(list_length(tidstate->tss_tideval) * sizeof(ItemPointerData));
58 itemptr = (ItemPointer)
59 DatumGetPointer(ExecEvalExprSwitchContext(lfirst(l),
63 if (!isNull && itemptr && ItemPointerIsValid(itemptr))
65 tidList[numTids] = *itemptr;
70 tidstate->tss_TidList = tidList;
71 tidstate->tss_NumTids = numTids;
72 tidstate->tss_TidPtr = -1;
75 /* ----------------------------------------------------------------
78 * Retrieve a tuple from the TidScan node's currentRelation
79 * using the tids in the TidScanState information.
81 * ----------------------------------------------------------------
83 static TupleTableSlot *
84 TidNext(TidScanState *node)
87 ScanDirection direction;
89 Relation heapRelation;
93 Buffer buffer = InvalidBuffer;
94 ItemPointerData *tidList;
100 * extract necessary information from tid scan node
102 estate = node->ss.ps.state;
103 direction = estate->es_direction;
104 snapshot = estate->es_snapshot;
105 heapRelation = node->ss.ss_currentRelation;
106 slot = node->ss.ss_ScanTupleSlot;
107 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
110 * Clear any reference to the previously returned tuple. This doesn't
111 * offer any great performance benefit, but it keeps this code in sync
112 * with SeqNext and IndexNext.
114 ExecClearTuple(slot);
117 * Check if we are evaluating PlanQual for tuple of this relation.
118 * Additional checking is not good, but no other way for now. We could
119 * introduce new nodes for this case and handle TidScan --> NewNode
120 * switching in Init/ReScan plan...
122 if (estate->es_evTuple != NULL &&
123 estate->es_evTuple[scanrelid - 1] != NULL)
125 if (estate->es_evTupleNull[scanrelid - 1])
126 return slot; /* return empty slot */
129 * XXX shouldn't we check here to make sure tuple matches TID
130 * list? In runtime-key case this is not certain, is it?
133 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
134 slot, InvalidBuffer, false);
136 /* Flag for the next call that no more tuples */
137 estate->es_evTupleNull[scanrelid - 1] = true;
142 * First time through, compute the list of TIDs to be visited
144 if (node->tss_TidList == NULL)
147 tidList = node->tss_TidList;
148 numTids = node->tss_NumTids;
150 tuple = &(node->tss_htup);
153 * ok, now that we have what we need, fetch an tid tuple. if scanning
154 * this tid succeeded then return the appropriate heap tuple.. else
157 bBackward = ScanDirectionIsBackward(direction);
160 tidNumber = numTids - node->tss_TidPtr - 1;
164 node->tss_TidPtr = numTids - 1;
169 if ((tidNumber = node->tss_TidPtr) < 0)
172 node->tss_TidPtr = 0;
175 while (tidNumber < numTids)
177 bool slot_is_valid = false;
179 tuple->t_self = tidList[node->tss_TidPtr];
180 if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
182 bool prev_matches = false;
186 * store the scanned tuple in the scan tuple slot of the scan
187 * state. Eventually we will only do this and not return a
188 * tuple. Note: we pass 'false' because tuples returned by
189 * amgetnext are pointers onto disk pages and were not created
190 * with palloc() and so should not be pfree()'d.
192 ExecStoreTuple(tuple, /* tuple to store */
193 slot, /* slot to store in */
194 buffer, /* buffer associated with tuple */
195 false); /* don't pfree */
198 * At this point we have an extra pin on the buffer, because
199 * ExecStoreTuple incremented the pin count. Drop our local
202 ReleaseBuffer(buffer);
205 * We must check to see if the current tuple would have been
206 * matched by an earlier tid, so we don't double report it.
208 for (prev_tid = 0; prev_tid < node->tss_TidPtr;
211 if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
218 slot_is_valid = true;
220 ExecClearTuple(slot);
232 * if we get here it means the tid scan failed so we are at the end of
235 return ExecClearTuple(slot);
238 /* ----------------------------------------------------------------
241 * Scans the relation using tids and returns
242 * the next qualifying tuple in the direction specified.
243 * It calls ExecScan() and passes it the access methods which returns
244 * the next tuple using the tids.
247 * -- the "cursor" maintained by the AMI is positioned at the tuple
248 * returned previously.
251 * -- the relation indicated is opened for scanning so that the
252 * "cursor" is positioned before the first qualifying tuple.
253 * -- tidPtr points to the first tid.
254 * -- state variable ruleFlag = nil.
255 * ----------------------------------------------------------------
258 ExecTidScan(TidScanState *node)
261 * use TidNext as access method
263 return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
266 /* ----------------------------------------------------------------
267 * ExecTidReScan(node)
268 * ----------------------------------------------------------------
271 ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
276 estate = node->ss.ps.state;
277 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
279 /* If we are being passed an outer tuple, save it for runtime key calc */
280 if (exprCtxt != NULL)
281 node->ss.ps.ps_ExprContext->ecxt_outertuple =
282 exprCtxt->ecxt_outertuple;
284 /* If this is re-scanning of PlanQual ... */
285 if (estate->es_evTuple != NULL &&
286 estate->es_evTuple[scanrelid - 1] != NULL)
288 estate->es_evTupleNull[scanrelid - 1] = false;
292 if (node->tss_TidList)
293 pfree(node->tss_TidList);
294 node->tss_TidList = NULL;
295 node->tss_NumTids = 0;
296 node->tss_TidPtr = -1;
299 /* ----------------------------------------------------------------
302 * Releases any storage allocated through C routines.
304 * ----------------------------------------------------------------
307 ExecEndTidScan(TidScanState *node)
310 * Free the exprcontext
312 ExecFreeExprContext(&node->ss.ps);
315 * clear out tuple table slots
317 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
318 ExecClearTuple(node->ss.ss_ScanTupleSlot);
321 * close the heap relation.
323 * Currently, we do not release the AccessShareLock acquired by
324 * ExecInitTidScan. This lock should be held till end of transaction.
325 * (There is a faction that considers this too much locking, however.)
327 heap_close(node->ss.ss_currentRelation, NoLock);
330 /* ----------------------------------------------------------------
333 * Marks scan position by marking the current tid.
335 * ----------------------------------------------------------------
338 ExecTidMarkPos(TidScanState *node)
340 node->tss_MarkTidPtr = node->tss_TidPtr;
343 /* ----------------------------------------------------------------
346 * Restores scan position by restoring the current tid.
349 * XXX Assumes previously marked scan position belongs to current tid
350 * ----------------------------------------------------------------
353 ExecTidRestrPos(TidScanState *node)
355 node->tss_TidPtr = node->tss_MarkTidPtr;
358 /* ----------------------------------------------------------------
361 * Initializes the tid scan's state information, creates
362 * scan keys, and opens the base and tid relations.
365 * node: TidNode node produced by the planner.
366 * estate: the execution state initialized in InitPlan.
367 * ----------------------------------------------------------------
370 ExecInitTidScan(TidScan *node, EState *estate)
372 TidScanState *tidstate;
374 RangeTblEntry *rtentry;
377 Relation currentRelation;
378 Bitmapset *execParam = NULL;
381 * create state structure
383 tidstate = makeNode(TidScanState);
384 tidstate->ss.ps.plan = (Plan *) node;
385 tidstate->ss.ps.state = estate;
388 * Miscellaneous initialization
390 * create expression context for node
392 ExecAssignExprContext(estate, &tidstate->ss.ps);
395 * initialize child expressions
397 tidstate->ss.ps.targetlist = (List *)
398 ExecInitExpr((Expr *) node->scan.plan.targetlist,
399 (PlanState *) tidstate);
400 tidstate->ss.ps.qual = (List *)
401 ExecInitExpr((Expr *) node->scan.plan.qual,
402 (PlanState *) tidstate);
404 tidstate->tss_tideval = (List *)
405 ExecInitExpr((Expr *) node->tideval,
406 (PlanState *) tidstate);
408 #define TIDSCAN_NSLOTS 2
411 * tuple table initialization
413 ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
414 ExecInitScanTupleSlot(estate, &tidstate->ss);
417 * mark tid list as not computed yet
419 tidstate->tss_TidList = NULL;
420 tidstate->tss_NumTids = 0;
421 tidstate->tss_TidPtr = -1;
424 * get the range table and direction information from the execution
425 * state (these are needed to open the relations).
427 rangeTable = estate->es_range_table;
430 * open the base relation
432 * We acquire AccessShareLock for the duration of the scan.
434 relid = node->scan.scanrelid;
435 rtentry = rt_fetch(relid, rangeTable);
436 reloid = rtentry->relid;
438 currentRelation = heap_open(reloid, AccessShareLock);
440 tidstate->ss.ss_currentRelation = currentRelation;
441 tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
444 * get the scan type from the relation descriptor.
446 ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
449 * if there are some PARAM_EXEC in skankeys then force tid rescan on
452 tidstate->ss.ps.chgParam = execParam;
455 * Initialize result tuple type and projection info.
457 ExecAssignResultTypeFromTL(&tidstate->ss.ps);
458 ExecAssignScanProjectionInfo(&tidstate->ss);
467 ExecCountSlotsTidScan(TidScan *node)
469 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
470 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;