1 /*-------------------------------------------------------------------------
4 * Routines to support direct tid scans of relations
6 * Portions Copyright (c) 1996-2001, 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.18 2001/06/22 19:16:22 wieck 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.
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 *, ItemPointer *);
33 static TupleTableSlot *TidNext(TidScan *node);
36 TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *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(TidScan *node)
71 CommonScanState *scanstate;
72 TidScanState *tidstate;
73 ScanDirection direction;
75 Relation heapRelation;
78 Buffer buffer = InvalidBuffer;
87 * extract necessary information from tid scan node
89 estate = node->scan.plan.state;
90 direction = estate->es_direction;
91 snapshot = estate->es_snapshot;
92 scanstate = node->scan.scanstate;
93 tidstate = node->tidstate;
94 heapRelation = scanstate->css_currentRelation;
95 numTids = tidstate->tss_NumTids;
96 tidList = tidstate->tss_TidList;
97 slot = scanstate->css_ScanTupleSlot;
100 * Check if we are evaluating PlanQual for tuple of this relation.
101 * Additional checking is not good, but no other way for now. We could
102 * introduce new nodes for this case and handle TidScan --> NewNode
103 * switching in Init/ReScan plan...
105 if (estate->es_evTuple != NULL &&
106 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
108 ExecClearTuple(slot);
109 if (estate->es_evTupleNull[node->scan.scanrelid - 1])
110 return slot; /* return empty slot */
112 ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
113 slot, InvalidBuffer, false);
115 /* Flag for the next call that no more tuples */
116 estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
120 tuple = &(tidstate->tss_htup);
123 * ok, now that we have what we need, fetch an tid tuple. if scanning
124 * this tid succeeded then return the appropriate heap tuple.. else
127 bBackward = ScanDirectionIsBackward(direction);
130 tidNumber = numTids - tidstate->tss_TidPtr - 1;
134 tidstate->tss_TidPtr = numTids - 1;
139 if ((tidNumber = tidstate->tss_TidPtr) < 0)
142 tidstate->tss_TidPtr = 0;
145 while (tidNumber < numTids)
147 bool slot_is_valid = false;
149 itemptr = tidList[tidstate->tss_TidPtr];
150 tuple->t_datamcxt = NULL;
151 tuple->t_data = NULL;
154 tuple->t_self = *(itemptr);
155 heap_fetch(heapRelation, snapshot, tuple, &buffer, NULL);
157 if (tuple->t_data != NULL)
159 bool prev_matches = false;
163 * store the scanned tuple in the scan tuple slot of the scan
164 * state. Eventually we will only do this and not return a
165 * tuple. Note: we pass 'false' because tuples returned by
166 * amgetnext are pointers onto disk pages and were not created
167 * with palloc() and so should not be pfree()'d.
169 ExecStoreTuple(tuple, /* tuple to store */
170 slot,/* slot to store in */
171 buffer, /* buffer associated with tuple */
172 false); /* don't pfree */
175 * At this point we have an extra pin on the buffer, because
176 * ExecStoreTuple incremented the pin count. Drop our local
179 ReleaseBuffer(buffer);
182 * We must check to see if the current tuple would have been
183 * matched by an earlier tid, so we don't double report it. We
184 * do this by passing the tuple through ExecQual and look for
185 * failure with all previous qualifications.
187 for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr;
190 if (ItemPointerEquals(tidList[prev_tid], &tuple->t_self))
197 slot_is_valid = true;
199 ExecClearTuple(slot);
201 else if (BufferIsValid(buffer))
202 ReleaseBuffer(buffer);
205 tidstate->tss_TidPtr--;
207 tidstate->tss_TidPtr++;
213 * if we get here it means the tid scan failed so we are at the end of
216 return ExecClearTuple(slot);
219 /* ----------------------------------------------------------------
222 * Scans the relation using tids and returns
223 * the next qualifying tuple in the direction specified.
224 * It calls ExecScan() and passes it the access methods which returns
225 * the next tuple using the tids.
228 * -- the "cursor" maintained by the AMI is positioned at the tuple
229 * returned previously.
232 * -- the relation indicated is opened for scanning so that the
233 * "cursor" is positioned before the first qualifying tuple.
234 * -- tidPtr points to the first tid.
235 * -- state variable ruleFlag = nil.
236 * ----------------------------------------------------------------
239 ExecTidScan(TidScan *node)
243 * use TidNext as access method
245 return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
248 /* ----------------------------------------------------------------
249 * ExecTidReScan(node)
250 * ----------------------------------------------------------------
253 ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
256 TidScanState *tidstate;
257 ItemPointer *tidList;
259 tidstate = node->tidstate;
260 estate = node->scan.plan.state;
261 tidstate->tss_TidPtr = -1;
262 tidList = tidstate->tss_TidList;
264 /* If we are being passed an outer tuple, save it for runtime key calc */
265 if (exprCtxt != NULL)
266 node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
267 exprCtxt->ecxt_outertuple;
269 /* If this is re-scanning of PlanQual ... */
270 if (estate->es_evTuple != NULL &&
271 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
273 estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
277 tidstate->tss_NumTids = TidListCreate(node->tideval,
278 node->scan.scanstate->cstate.cs_ExprContext,
282 * perhaps return something meaningful
287 /* ----------------------------------------------------------------
290 * Releases any storage allocated through C routines.
292 * ----------------------------------------------------------------
295 ExecEndTidScan(TidScan *node)
297 CommonScanState *scanstate;
298 TidScanState *tidstate;
300 scanstate = node->scan.scanstate;
301 tidstate = node->tidstate;
302 if (tidstate && tidstate->tss_TidList)
303 pfree(tidstate->tss_TidList);
306 * extract information from the node
310 * Free the projection info and the scan attribute info
312 * Note: we don't ExecFreeResultType(scanstate) because the rule manager
313 * depends on the tupType returned by ExecMain(). So for now, this is
314 * freed at end-transaction time. -cim 6/2/91
316 ExecFreeProjectionInfo(&scanstate->cstate);
317 ExecFreeExprContext(&scanstate->cstate);
320 * close the heap and tid relations
322 ExecCloseR((Plan *) node);
325 * clear out tuple table slots
327 ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
328 ExecClearTuple(scanstate->css_ScanTupleSlot);
331 /* ----------------------------------------------------------------
334 * Marks scan position by marking the current tid.
336 * ----------------------------------------------------------------
339 ExecTidMarkPos(TidScan *node)
341 TidScanState *tidstate;
343 tidstate = node->tidstate;
344 tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
348 /* ----------------------------------------------------------------
351 * Restores scan position by restoring the current tid.
354 * XXX Assumes previously marked scan position belongs to current tid
355 * ----------------------------------------------------------------
358 ExecTidRestrPos(TidScan *node)
360 TidScanState *tidstate;
362 tidstate = node->tidstate;
363 tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
368 /* ----------------------------------------------------------------
371 * Initializes the tid scan's state information, creates
372 * scan keys, and opens the base and tid relations.
375 * node: TidNode node produced by the planner.
376 * estate: the execution state initialized in InitPlan.
377 * ----------------------------------------------------------------
380 ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
382 TidScanState *tidstate;
383 CommonScanState *scanstate;
384 ItemPointer *tidList;
388 RangeTblEntry *rtentry;
391 Relation currentRelation;
392 List *execParam = NIL;
395 * assign execution state to node
397 node->scan.plan.state = estate;
400 * Part 1) initialize scan state
402 * create new CommonScanState for node
404 scanstate = makeNode(CommonScanState);
405 node->scan.scanstate = scanstate;
408 * Miscellaneous initialization
410 * create expression context for node
412 ExecAssignExprContext(estate, &scanstate->cstate);
414 #define TIDSCAN_NSLOTS 2
417 * tuple table initialization
419 ExecInitResultTupleSlot(estate, &scanstate->cstate);
420 ExecInitScanTupleSlot(estate, scanstate);
423 * initialize projection info. result type comes from scan desc
426 ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
429 * Part 2) initialize tid scan state
431 * create new TidScanState for node
433 tidstate = makeNode(TidScanState);
434 node->tidstate = tidstate;
437 * get the tid node information
439 tidList = (ItemPointer *) palloc(length(node->tideval) * sizeof(ItemPointer));
441 if (!node->needRescan)
442 numTids = TidListCreate(node->tideval, scanstate->cstate.cs_ExprContext, tidList);
445 CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
447 tidstate->tss_NumTids = numTids;
448 tidstate->tss_TidPtr = tidPtr;
449 tidstate->tss_TidList = tidList;
452 * get the range table and direction information from the execution
453 * state (these are needed to open the relations).
455 rangeTable = estate->es_range_table;
458 * open the base relation
460 relid = node->scan.scanrelid;
461 rtentry = rt_fetch(relid, rangeTable);
462 reloid = rtentry->relid;
464 currentRelation = heap_open(reloid, AccessShareLock);
465 scanstate->css_currentRelation = currentRelation;
466 scanstate->css_currentScanDesc = 0;
469 * get the scan type from the relation descriptor.
471 ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
472 ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
475 * if there are some PARAM_EXEC in skankeys then force tid rescan on
478 ((Plan *) node)->chgParam = execParam;
487 ExecCountSlotsTidScan(TidScan *node)
489 return ExecCountSlotsNode(outerPlan((Plan *) node)) +
490 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;