]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeTidscan.c
Cause ExecCountSlots() accounting to bear some relationship to reality.
[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-2001, 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.17 2001/05/27 20:42:20 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  *
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 *, ItemPointer *);
33 static TupleTableSlot *TidNext(TidScan *node);
34
35 static int
36 TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *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(TidScan *node)
69 {
70         EState     *estate;
71         CommonScanState *scanstate;
72         TidScanState *tidstate;
73         ScanDirection direction;
74         Snapshot        snapshot;
75         Relation        heapRelation;
76         HeapTuple       tuple;
77         TupleTableSlot *slot;
78         Buffer          buffer = InvalidBuffer;
79         int                     numTids;
80
81         bool            bBackward;
82         int                     tidNumber;
83         ItemPointer *tidList,
84                                 itemptr;
85
86         /*
87          * extract necessary information from tid scan node
88          */
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;
98
99         /*
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...
104          */
105         if (estate->es_evTuple != NULL &&
106                 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
107         {
108                 ExecClearTuple(slot);
109                 if (estate->es_evTupleNull[node->scan.scanrelid - 1])
110                         return slot;            /* return empty slot */
111
112                 ExecStoreTuple(estate->es_evTuple[node->scan.scanrelid - 1],
113                                            slot, InvalidBuffer, false);
114
115                 /* Flag for the next call that no more tuples */
116                 estate->es_evTupleNull[node->scan.scanrelid - 1] = true;
117                 return (slot);
118         }
119
120         tuple = &(tidstate->tss_htup);
121
122         /*
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
125          * return NULL.
126          */
127         bBackward = ScanDirectionIsBackward(direction);
128         if (bBackward)
129         {
130                 tidNumber = numTids - tidstate->tss_TidPtr - 1;
131                 if (tidNumber < 0)
132                 {
133                         tidNumber = 0;
134                         tidstate->tss_TidPtr = numTids - 1;
135                 }
136         }
137         else
138         {
139                 if ((tidNumber = tidstate->tss_TidPtr) < 0)
140                 {
141                         tidNumber = 0;
142                         tidstate->tss_TidPtr = 0;
143                 }
144         }
145         while (tidNumber < numTids)
146         {
147                 bool            slot_is_valid = false;
148
149                 itemptr = tidList[tidstate->tss_TidPtr];
150                 tuple->t_datamcxt = NULL;
151                 tuple->t_data = NULL;
152                 if (itemptr)
153                 {
154                         tuple->t_self = *(itemptr);
155                         heap_fetch(heapRelation, snapshot, tuple, &buffer);
156                 }
157                 if (tuple->t_data != NULL)
158                 {
159                         bool            prev_matches = false;
160                         int                     prev_tid;
161
162                         /*
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.
168                          */
169                         ExecStoreTuple(tuple,           /* tuple to store */
170                                                    slot,/* slot to store in */
171                                                    buffer,              /* buffer associated with tuple  */
172                                                    false);              /* don't pfree */
173
174                         /*
175                          * At this point we have an extra pin on the buffer, because
176                          * ExecStoreTuple incremented the pin count. Drop our local
177                          * pin.
178                          */
179                         ReleaseBuffer(buffer);
180
181                         /*
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.
186                          */
187                         for (prev_tid = 0; prev_tid < tidstate->tss_TidPtr;
188                                  prev_tid++)
189                         {
190                                 if (ItemPointerEquals(tidList[prev_tid], &tuple->t_self))
191                                 {
192                                         prev_matches = true;
193                                         break;
194                                 }
195                         }
196                         if (!prev_matches)
197                                 slot_is_valid = true;
198                         else
199                                 ExecClearTuple(slot);
200                 }
201                 else if (BufferIsValid(buffer))
202                         ReleaseBuffer(buffer);
203                 tidNumber++;
204                 if (bBackward)
205                         tidstate->tss_TidPtr--;
206                 else
207                         tidstate->tss_TidPtr++;
208                 if (slot_is_valid)
209                         return slot;
210         }
211
212         /*
213          * if we get here it means the tid scan failed so we are at the end of
214          * the scan..
215          */
216         return ExecClearTuple(slot);
217 }
218
219 /* ----------------------------------------------------------------
220  *              ExecTidScan(node)
221  *
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.
226  *
227  *              Conditions:
228  *                -- the "cursor" maintained by the AMI is positioned at the tuple
229  *                       returned previously.
230  *
231  *              Initial States:
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  * ----------------------------------------------------------------
237  */
238 TupleTableSlot *
239 ExecTidScan(TidScan *node)
240 {
241
242         /*
243          * use TidNext as access method
244          */
245         return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
246 }
247
248 /* ----------------------------------------------------------------
249  *              ExecTidReScan(node)
250  * ----------------------------------------------------------------
251  */
252 void
253 ExecTidReScan(TidScan *node, ExprContext *exprCtxt, Plan *parent)
254 {
255         EState     *estate;
256         TidScanState *tidstate;
257         ItemPointer *tidList;
258
259         tidstate = node->tidstate;
260         estate = node->scan.plan.state;
261         tidstate->tss_TidPtr = -1;
262         tidList = tidstate->tss_TidList;
263
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;
268
269         /* If this is re-scanning of PlanQual ... */
270         if (estate->es_evTuple != NULL &&
271                 estate->es_evTuple[node->scan.scanrelid - 1] != NULL)
272         {
273                 estate->es_evTupleNull[node->scan.scanrelid - 1] = false;
274                 return;
275         }
276
277         tidstate->tss_NumTids = TidListCreate(node->tideval,
278                                                          node->scan.scanstate->cstate.cs_ExprContext,
279                                                                                   tidList);
280
281         /*
282          * perhaps return something meaningful
283          */
284         return;
285 }
286
287 /* ----------------------------------------------------------------
288  *              ExecEndTidScan
289  *
290  *              Releases any storage allocated through C routines.
291  *              Returns nothing.
292  * ----------------------------------------------------------------
293  */
294 void
295 ExecEndTidScan(TidScan *node)
296 {
297         CommonScanState *scanstate;
298         TidScanState *tidstate;
299
300         scanstate = node->scan.scanstate;
301         tidstate = node->tidstate;
302         if (tidstate && tidstate->tss_TidList)
303                 pfree(tidstate->tss_TidList);
304
305         /*
306          * extract information from the node
307          */
308
309         /*
310          * Free the projection info and the scan attribute info
311          *
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
315          */
316         ExecFreeProjectionInfo(&scanstate->cstate);
317         ExecFreeExprContext(&scanstate->cstate);
318
319         /*
320          * close the heap and tid relations
321          */
322         ExecCloseR((Plan *) node);
323
324         /*
325          * clear out tuple table slots
326          */
327         ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
328         ExecClearTuple(scanstate->css_ScanTupleSlot);
329 }
330
331 /* ----------------------------------------------------------------
332  *              ExecTidMarkPos
333  *
334  *              Marks scan position by marking the current tid.
335  *              Returns nothing.
336  * ----------------------------------------------------------------
337  */
338 void
339 ExecTidMarkPos(TidScan *node)
340 {
341         TidScanState *tidstate;
342
343         tidstate = node->tidstate;
344         tidstate->tss_MarkTidPtr = tidstate->tss_TidPtr;
345 }
346
347 #ifdef NOT_USED
348 /* ----------------------------------------------------------------
349  *              ExecTidRestrPos
350  *
351  *              Restores scan position by restoring the current tid.
352  *              Returns nothing.
353  *
354  *              XXX Assumes previously marked scan position belongs to current tid
355  * ----------------------------------------------------------------
356  */
357 void
358 ExecTidRestrPos(TidScan *node)
359 {
360         TidScanState *tidstate;
361
362         tidstate = node->tidstate;
363         tidstate->tss_TidPtr = tidstate->tss_MarkTidPtr;
364 }
365
366 #endif
367
368 /* ----------------------------------------------------------------
369  *              ExecInitTidScan
370  *
371  *              Initializes the tid scan's state information, creates
372  *              scan keys, and opens the base and tid relations.
373  *
374  *              Parameters:
375  *                node: TidNode node produced by the planner.
376  *                estate: the execution state initialized in InitPlan.
377  * ----------------------------------------------------------------
378  */
379 bool
380 ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
381 {
382         TidScanState *tidstate;
383         CommonScanState *scanstate;
384         ItemPointer *tidList;
385         int                     numTids;
386         int                     tidPtr;
387         List       *rangeTable;
388         RangeTblEntry *rtentry;
389         Oid                     relid;
390         Oid                     reloid;
391         Relation        currentRelation;
392         List       *execParam = NIL;
393
394         /*
395          * assign execution state to node
396          */
397         node->scan.plan.state = estate;
398
399         /*
400          * Part 1)      initialize scan state
401          *
402          * create new CommonScanState for node
403          */
404         scanstate = makeNode(CommonScanState);
405         node->scan.scanstate = scanstate;
406
407         /*
408          * Miscellaneous initialization
409          *
410          * create expression context for node
411          */
412         ExecAssignExprContext(estate, &scanstate->cstate);
413
414 #define TIDSCAN_NSLOTS 2
415
416         /*
417          * tuple table initialization
418          */
419         ExecInitResultTupleSlot(estate, &scanstate->cstate);
420         ExecInitScanTupleSlot(estate, scanstate);
421
422         /*
423          * initialize projection info.  result type comes from scan desc
424          * below..
425          */
426         ExecAssignProjectionInfo((Plan *) node, &scanstate->cstate);
427
428         /*
429          * Part 2)      initialize tid scan state
430          *
431          * create new TidScanState for node
432          */
433         tidstate = makeNode(TidScanState);
434         node->tidstate = tidstate;
435
436         /*
437          * get the tid node information
438          */
439         tidList = (ItemPointer *) palloc(length(node->tideval) * sizeof(ItemPointer));
440         numTids = 0;
441         if (!node->needRescan)
442                 numTids = TidListCreate(node->tideval, scanstate->cstate.cs_ExprContext, tidList);
443         tidPtr = -1;
444
445         CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
446
447         tidstate->tss_NumTids = numTids;
448         tidstate->tss_TidPtr = tidPtr;
449         tidstate->tss_TidList = tidList;
450
451         /*
452          * get the range table and direction information from the execution
453          * state (these are needed to open the relations).
454          */
455         rangeTable = estate->es_range_table;
456
457         /*
458          * open the base relation
459          */
460         relid = node->scan.scanrelid;
461         rtentry = rt_fetch(relid, rangeTable);
462         reloid = rtentry->relid;
463
464         currentRelation = heap_open(reloid, AccessShareLock);
465         scanstate->css_currentRelation = currentRelation;
466         scanstate->css_currentScanDesc = 0;
467
468         /*
469          * get the scan type from the relation descriptor.
470          */
471         ExecAssignScanType(scanstate, RelationGetDescr(currentRelation), false);
472         ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
473
474         /*
475          * if there are some PARAM_EXEC in skankeys then force tid rescan on
476          * first scan.
477          */
478         ((Plan *) node)->chgParam = execParam;
479
480         /*
481          * all done.
482          */
483         return TRUE;
484 }
485
486 int
487 ExecCountSlotsTidScan(TidScan *node)
488 {
489         return ExecCountSlotsNode(outerPlan((Plan *) node)) +
490         ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
491 }