]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeTidscan.c
Phase 1 of read-only-plans project: cause executor state nodes to point
[postgresql] / src / backend / executor / nodeTidscan.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeTidscan.c
4  *        Routines to support direct tid scans of relations
5  *
6  * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.28 2002/12/05 15:50:34 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  * INTERFACE ROUTINES
17  *
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.
24  */
25 #include "postgres.h"
26
27 #include "executor/execdebug.h"
28 #include "executor/nodeTidscan.h"
29 #include "access/heapam.h"
30 #include "parser/parsetree.h"
31
32 static int      TidListCreate(List *, ExprContext *, ItemPointerData[]);
33 static TupleTableSlot *TidNext(TidScanState *node);
34
35 static int
36 TidListCreate(List *evalList, ExprContext *econtext, ItemPointerData tidList[])
37 {
38         List       *lst;
39         ItemPointer itemptr;
40         bool            isNull;
41         int                     numTids = 0;
42
43         foreach(lst, evalList)
44         {
45                 itemptr = (ItemPointer)
46                         DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
47                                                                                                           econtext,
48                                                                                                           &isNull,
49                                                                                                           NULL));
50                 if (!isNull && itemptr && ItemPointerIsValid(itemptr))
51                 {
52                         tidList[numTids] = *itemptr;
53                         numTids++;
54                 }
55         }
56         return numTids;
57 }
58
59 /* ----------------------------------------------------------------
60  *              TidNext
61  *
62  *              Retrieve a tuple from the TidScan node's currentRelation
63  *              using the tids in the TidScanState information.
64  *
65  * ----------------------------------------------------------------
66  */
67 static TupleTableSlot *
68 TidNext(TidScanState *node)
69 {
70         EState     *estate;
71         ScanDirection direction;
72         Snapshot        snapshot;
73         Relation        heapRelation;
74         HeapTuple       tuple;
75         TupleTableSlot *slot;
76         Index           scanrelid;
77         Buffer          buffer = InvalidBuffer;
78         int                     numTids;
79         bool            bBackward;
80         int                     tidNumber;
81         ItemPointerData *tidList;
82
83         /*
84          * extract necessary information from tid scan node
85          */
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;
94
95         /*
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...
100          */
101         if (estate->es_evTuple != NULL &&
102                 estate->es_evTuple[scanrelid - 1] != NULL)
103         {
104                 ExecClearTuple(slot);
105                 if (estate->es_evTupleNull[scanrelid - 1])
106                         return slot;            /* return empty slot */
107
108                 /*
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?
111                  */
112
113                 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
114                                            slot, InvalidBuffer, false);
115
116                 /* Flag for the next call that no more tuples */
117                 estate->es_evTupleNull[scanrelid - 1] = true;
118                 return (slot);
119         }
120
121         tuple = &(node->tss_htup);
122
123         /*
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
126          * return NULL.
127          */
128         bBackward = ScanDirectionIsBackward(direction);
129         if (bBackward)
130         {
131                 tidNumber = numTids - node->tss_TidPtr - 1;
132                 if (tidNumber < 0)
133                 {
134                         tidNumber = 0;
135                         node->tss_TidPtr = numTids - 1;
136                 }
137         }
138         else
139         {
140                 if ((tidNumber = node->tss_TidPtr) < 0)
141                 {
142                         tidNumber = 0;
143                         node->tss_TidPtr = 0;
144                 }
145         }
146         while (tidNumber < numTids)
147         {
148                 bool            slot_is_valid = false;
149
150                 tuple->t_self = tidList[node->tss_TidPtr];
151                 if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
152                 {
153                         bool            prev_matches = false;
154                         int                     prev_tid;
155
156                         /*
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.
162                          */
163                         ExecStoreTuple(tuple,           /* tuple to store */
164                                                    slot,        /* slot to store in */
165                                                    buffer,              /* buffer associated with tuple  */
166                                                    false);              /* don't pfree */
167
168                         /*
169                          * At this point we have an extra pin on the buffer, because
170                          * ExecStoreTuple incremented the pin count. Drop our local
171                          * pin.
172                          */
173                         ReleaseBuffer(buffer);
174
175                         /*
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.
180                          */
181                         for (prev_tid = 0; prev_tid < node->tss_TidPtr;
182                                  prev_tid++)
183                         {
184                                 if (ItemPointerEquals(&tidList[prev_tid], &tuple->t_self))
185                                 {
186                                         prev_matches = true;
187                                         break;
188                                 }
189                         }
190                         if (!prev_matches)
191                                 slot_is_valid = true;
192                         else
193                                 ExecClearTuple(slot);
194                 }
195                 tidNumber++;
196                 if (bBackward)
197                         node->tss_TidPtr--;
198                 else
199                         node->tss_TidPtr++;
200                 if (slot_is_valid)
201                         return slot;
202         }
203
204         /*
205          * if we get here it means the tid scan failed so we are at the end of
206          * the scan..
207          */
208         return ExecClearTuple(slot);
209 }
210
211 /* ----------------------------------------------------------------
212  *              ExecTidScan(node)
213  *
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.
218  *
219  *              Conditions:
220  *                -- the "cursor" maintained by the AMI is positioned at the tuple
221  *                       returned previously.
222  *
223  *              Initial States:
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  * ----------------------------------------------------------------
229  */
230 TupleTableSlot *
231 ExecTidScan(TidScanState *node)
232 {
233         /*
234          * use TidNext as access method
235          */
236         return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
237 }
238
239 /* ----------------------------------------------------------------
240  *              ExecTidReScan(node)
241  * ----------------------------------------------------------------
242  */
243 void
244 ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
245 {
246         EState     *estate;
247         ItemPointerData *tidList;
248         Index           scanrelid;
249
250         estate = node->ss.ps.state;
251         tidList = node->tss_TidList;
252         scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
253
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;
258
259         /* If this is re-scanning of PlanQual ... */
260         if (estate->es_evTuple != NULL &&
261                 estate->es_evTuple[scanrelid - 1] != NULL)
262         {
263                 estate->es_evTupleNull[scanrelid - 1] = false;
264                 return;
265         }
266
267         node->tss_TidPtr = -1;
268 }
269
270 /* ----------------------------------------------------------------
271  *              ExecEndTidScan
272  *
273  *              Releases any storage allocated through C routines.
274  *              Returns nothing.
275  * ----------------------------------------------------------------
276  */
277 void
278 ExecEndTidScan(TidScanState *node)
279 {
280         /*
281          * extract information from the node
282          */
283         if (node && node->tss_TidList)
284                 pfree(node->tss_TidList);
285
286         /*
287          * Free the projection info and the scan attribute info
288          *
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
292          */
293         ExecFreeProjectionInfo(&node->ss.ps);
294         ExecFreeExprContext(&node->ss.ps);
295
296         /*
297          * clear out tuple table slots
298          */
299         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
300         ExecClearTuple(node->ss.ss_ScanTupleSlot);
301
302         /*
303          * close the heap relation.
304          *
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.)
308          */
309         heap_close(node->ss.ss_currentRelation, NoLock);
310 }
311
312 /* ----------------------------------------------------------------
313  *              ExecTidMarkPos
314  *
315  *              Marks scan position by marking the current tid.
316  *              Returns nothing.
317  * ----------------------------------------------------------------
318  */
319 void
320 ExecTidMarkPos(TidScanState *node)
321 {
322         node->tss_MarkTidPtr = node->tss_TidPtr;
323 }
324
325 /* ----------------------------------------------------------------
326  *              ExecTidRestrPos
327  *
328  *              Restores scan position by restoring the current tid.
329  *              Returns nothing.
330  *
331  *              XXX Assumes previously marked scan position belongs to current tid
332  * ----------------------------------------------------------------
333  */
334 void
335 ExecTidRestrPos(TidScanState *node)
336 {
337         node->tss_TidPtr = node->tss_MarkTidPtr;
338 }
339
340 /* ----------------------------------------------------------------
341  *              ExecInitTidScan
342  *
343  *              Initializes the tid scan's state information, creates
344  *              scan keys, and opens the base and tid relations.
345  *
346  *              Parameters:
347  *                node: TidNode node produced by the planner.
348  *                estate: the execution state initialized in InitPlan.
349  * ----------------------------------------------------------------
350  */
351 TidScanState *
352 ExecInitTidScan(TidScan *node, EState *estate)
353 {
354         TidScanState *tidstate;
355         ItemPointerData *tidList;
356         int                     numTids;
357         int                     tidPtr;
358         List       *rangeTable;
359         RangeTblEntry *rtentry;
360         Oid                     relid;
361         Oid                     reloid;
362         Relation        currentRelation;
363         List       *execParam = NIL;
364
365         /*
366          * create state structure
367          */
368         tidstate = makeNode(TidScanState);
369         tidstate->ss.ps.plan = (Plan *) node;
370         tidstate->ss.ps.state = estate;
371
372         /*
373          * Miscellaneous initialization
374          *
375          * create expression context for node
376          */
377         ExecAssignExprContext(estate, &tidstate->ss.ps);
378
379         /*
380          * initialize child expressions
381          */
382         tidstate->ss.ps.targetlist = (List *)
383                 ExecInitExpr((Node *) node->scan.plan.targetlist,
384                                          (PlanState *) tidstate);
385         tidstate->ss.ps.qual = (List *)
386                 ExecInitExpr((Node *) node->scan.plan.qual,
387                                          (PlanState *) tidstate);
388
389 #define TIDSCAN_NSLOTS 2
390
391         /*
392          * tuple table initialization
393          */
394         ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
395         ExecInitScanTupleSlot(estate, &tidstate->ss);
396
397         /*
398          * initialize projection info.  result type comes from scan desc
399          * below..
400          */
401         ExecAssignProjectionInfo(&tidstate->ss.ps);
402
403         /*
404          * get the tid node information
405          */
406         tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
407         numTids = TidListCreate(node->tideval,
408                                                         tidstate->ss.ps.ps_ExprContext,
409                                                         tidList);
410         tidPtr = -1;
411
412         CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
413
414         tidstate->tss_NumTids = numTids;
415         tidstate->tss_TidPtr = tidPtr;
416         tidstate->tss_TidList = tidList;
417
418         /*
419          * get the range table and direction information from the execution
420          * state (these are needed to open the relations).
421          */
422         rangeTable = estate->es_range_table;
423
424         /*
425          * open the base relation
426          *
427          * We acquire AccessShareLock for the duration of the scan.
428          */
429         relid = node->scan.scanrelid;
430         rtentry = rt_fetch(relid, rangeTable);
431         reloid = rtentry->relid;
432
433         currentRelation = heap_open(reloid, AccessShareLock);
434
435         tidstate->ss.ss_currentRelation = currentRelation;
436         tidstate->ss.ss_currentScanDesc = NULL;         /* no heap scan here */
437
438         /*
439          * get the scan type from the relation descriptor.
440          */
441         ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
442         ExecAssignResultTypeFromTL(&tidstate->ss.ps);
443
444         /*
445          * if there are some PARAM_EXEC in skankeys then force tid rescan on
446          * first scan.
447          */
448         tidstate->ss.ps.chgParam = execParam;
449
450         /*
451          * all done.
452          */
453         return tidstate;
454 }
455
456 int
457 ExecCountSlotsTidScan(TidScan *node)
458 {
459         return ExecCountSlotsNode(outerPlan((Plan *) node)) +
460                 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
461 }