1 /*-------------------------------------------------------------------------
4 * Routines to support direct tid scans of relations
6 * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.29 2002/12/13 19:45: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 "executor/execdebug.h"
28 #include "executor/nodeTidscan.h"
29 #include "access/heapam.h"
30 #include "parser/parsetree.h"
32 static int TidListCreate(List *, ExprContext *, ItemPointerData[]);
33 static TupleTableSlot *TidNext(TidScanState *node);
36 TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[])
43 foreach(lst, evalList)
45 itemptr = (ItemPointer)
46 DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
50 if (!isNull && itemptr && ItemPointerIsValid(itemptr))
52 tidList[numTids] = *itemptr;
59 /* ----------------------------------------------------------------
62 * Retrieve a tuple from the TidScan node's currentRelation
63 * using the tids in the TidScanState information.
65 * ----------------------------------------------------------------
67 static TupleTableSlot *
68 TidNext(TidScanState *node)
71 ScanDirection direction;
73 Relation heapRelation;
77 Buffer buffer = InvalidBuffer;
81 ItemPointerData *tidList;
84 * extract necessary information from tid scan node
86 estate = node->ss.ps.state;
87 direction = estate->es_direction;
88 snapshot = estate->es_snapshot;
89 heapRelation = node->ss.ss_currentRelation;
90 numTids = node->tss_NumTids;
91 tidList = node->tss_TidList;
92 slot = node->ss.ss_ScanTupleSlot;
93 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
96 * Check if we are evaluating PlanQual for tuple of this relation.
97 * Additional checking is not good, but no other way for now. We could
98 * introduce new nodes for this case and handle TidScan --> NewNode
99 * switching in Init/ReScan plan...
101 if (estate->es_evTuple != NULL &&
102 estate->es_evTuple[scanrelid - 1] != NULL)
104 ExecClearTuple(slot);
105 if (estate->es_evTupleNull[scanrelid - 1])
106 return slot; /* return empty slot */
109 * XXX shouldn't we check here to make sure tuple matches TID
110 * list? In runtime-key case this is not certain, is it?
113 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
114 slot, InvalidBuffer, false);
116 /* Flag for the next call that no more tuples */
117 estate->es_evTupleNull[scanrelid - 1] = true;
121 tuple = &(node->tss_htup);
124 * ok, now that we have what we need, fetch an tid tuple. if scanning
125 * this tid succeeded then return the appropriate heap tuple.. else
128 bBackward = ScanDirectionIsBackward(direction);
131 tidNumber = numTids - node->tss_TidPtr - 1;
135 node->tss_TidPtr = numTids - 1;
140 if ((tidNumber = node->tss_TidPtr) < 0)
143 node->tss_TidPtr = 0;
146 while (tidNumber < numTids)
148 bool slot_is_valid = false;
150 tuple->t_self = tidList[node->tss_TidPtr];
151 if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
153 bool prev_matches = false;
157 * store the scanned tuple in the scan tuple slot of the scan
158 * state. Eventually we will only do this and not return a
159 * tuple. Note: we pass 'false' because tuples returned by
160 * amgetnext are pointers onto disk pages and were not created
161 * with palloc() and so should not be pfree()'d.
163 ExecStoreTuple(tuple, /* tuple to store */
164 slot, /* slot to store in */
165 buffer, /* buffer associated with tuple */
166 false); /* don't pfree */
169 * At this point we have an extra pin on the buffer, because
170 * ExecStoreTuple incremented the pin count. Drop our local
173 ReleaseBuffer(buffer);
176 * We must check to see if the current tuple would have been
177 * matched by an earlier tid, so we don't double report it. We
178 * do this by passing the tuple through ExecQual and look for
179 * failure with all previous qualifications.
181 for (prev_tid = 0; prev_tid < node->tss_TidPtr;
184 if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
191 slot_is_valid = true;
193 ExecClearTuple(slot);
205 * if we get here it means the tid scan failed so we are at the end of
208 return ExecClearTuple(slot);
211 /* ----------------------------------------------------------------
214 * Scans the relation using tids and returns
215 * the next qualifying tuple in the direction specified.
216 * It calls ExecScan() and passes it the access methods which returns
217 * the next tuple using the tids.
220 * -- the "cursor" maintained by the AMI is positioned at the tuple
221 * returned previously.
224 * -- the relation indicated is opened for scanning so that the
225 * "cursor" is positioned before the first qualifying tuple.
226 * -- tidPtr points to the first tid.
227 * -- state variable ruleFlag = nil.
228 * ----------------------------------------------------------------
231 ExecTidScan(TidScanState *node)
234 * use TidNext as access method
236 return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
239 /* ----------------------------------------------------------------
240 * ExecTidReScan(node)
241 * ----------------------------------------------------------------
244 ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
247 ItemPointerData *tidList;
250 estate = node->ss.ps.state;
251 tidList = node->tss_TidList;
252 scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
254 /* If we are being passed an outer tuple, save it for runtime key calc */
255 if (exprCtxt != NULL)
256 node->ss.ps.ps_ExprContext->ecxt_outertuple =
257 exprCtxt->ecxt_outertuple;
259 /* If this is re-scanning of PlanQual ... */
260 if (estate->es_evTuple != NULL &&
261 estate->es_evTuple[scanrelid - 1] != NULL)
263 estate->es_evTupleNull[scanrelid - 1] = false;
267 node->tss_TidPtr = -1;
270 /* ----------------------------------------------------------------
273 * Releases any storage allocated through C routines.
275 * ----------------------------------------------------------------
278 ExecEndTidScan(TidScanState *node)
281 * extract information from the node
283 if (node && node->tss_TidList)
284 pfree(node->tss_TidList);
287 * Free the projection info and the scan attribute info
289 * Note: we don't ExecFreeResultType(scanstate) because the rule manager
290 * depends on the tupType returned by ExecMain(). So for now, this is
291 * freed at end-transaction time. -cim 6/2/91
293 ExecFreeProjectionInfo(&node->ss.ps);
294 ExecFreeExprContext(&node->ss.ps);
297 * clear out tuple table slots
299 ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
300 ExecClearTuple(node->ss.ss_ScanTupleSlot);
303 * close the heap relation.
305 * Currently, we do not release the AccessShareLock acquired by
306 * ExecInitTidScan. This lock should be held till end of transaction.
307 * (There is a faction that considers this too much locking, however.)
309 heap_close(node->ss.ss_currentRelation, NoLock);
312 /* ----------------------------------------------------------------
315 * Marks scan position by marking the current tid.
317 * ----------------------------------------------------------------
320 ExecTidMarkPos(TidScanState *node)
322 node->tss_MarkTidPtr = node->tss_TidPtr;
325 /* ----------------------------------------------------------------
328 * Restores scan position by restoring the current tid.
331 * XXX Assumes previously marked scan position belongs to current tid
332 * ----------------------------------------------------------------
335 ExecTidRestrPos(TidScanState *node)
337 node->tss_TidPtr = node->tss_MarkTidPtr;
340 /* ----------------------------------------------------------------
343 * Initializes the tid scan's state information, creates
344 * scan keys, and opens the base and tid relations.
347 * node: TidNode node produced by the planner.
348 * estate: the execution state initialized in InitPlan.
349 * ----------------------------------------------------------------
352 ExecInitTidScan(TidScan *node, EState *estate)
354 TidScanState *tidstate;
355 ItemPointerData *tidList;
359 RangeTblEntry *rtentry;
362 Relation currentRelation;
363 List *execParam = NIL;
366 * create state structure
368 tidstate = makeNode(TidScanState);
369 tidstate->ss.ps.plan = (Plan *) node;
370 tidstate->ss.ps.state = estate;
373 * Miscellaneous initialization
375 * create expression context for node
377 ExecAssignExprContext(estate, &tidstate->ss.ps);
380 * initialize child expressions
382 tidstate->ss.ps.targetlist = (List *)
383 ExecInitExpr((Expr *) node->scan.plan.targetlist,
384 (PlanState *) tidstate);
385 tidstate->ss.ps.qual = (List *)
386 ExecInitExpr((Expr *) node->scan.plan.qual,
387 (PlanState *) tidstate);
389 #define TIDSCAN_NSLOTS 2
392 * tuple table initialization
394 ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
395 ExecInitScanTupleSlot(estate, &tidstate->ss);
398 * initialize projection info. result type comes from scan desc
401 ExecAssignProjectionInfo(&tidstate->ss.ps);
404 * get the tid node information
406 tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
407 tidstate->tss_tideval = (List *)
408 ExecInitExpr((Expr *) node->tideval,
409 (PlanState *) tidstate);
410 numTids = TidListCreate(tidstate->tss_tideval,
411 tidstate->ss.ps.ps_ExprContext,
415 CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
417 tidstate->tss_NumTids = numTids;
418 tidstate->tss_TidPtr = tidPtr;
419 tidstate->tss_TidList = tidList;
422 * get the range table and direction information from the execution
423 * state (these are needed to open the relations).
425 rangeTable = estate->es_range_table;
428 * open the base relation
430 * We acquire AccessShareLock for the duration of the scan.
432 relid = node->scan.scanrelid;
433 rtentry = rt_fetch(relid, rangeTable);
434 reloid = rtentry->relid;
436 currentRelation = heap_open(reloid, AccessShareLock);
438 tidstate->ss.ss_currentRelation = currentRelation;
439 tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
442 * get the scan type from the relation descriptor.
444 ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
445 ExecAssignResultTypeFromTL(&tidstate->ss.ps);
448 * if there are some PARAM_EXEC in skankeys then force tid rescan on
451 tidstate->ss.ps.chgParam = execParam;
460 ExecCountSlotsTidScan(TidScan *node)
462 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
463 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;