]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeTidscan.c
Fix wrong/misleading comments, be more consistent about where to call
[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.31 2003/01/12 22:01:38 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          * Free the exprcontext
282          */
283         ExecFreeExprContext(&node->ss.ps);
284
285         /*
286          * clear out tuple table slots
287          */
288         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
289         ExecClearTuple(node->ss.ss_ScanTupleSlot);
290
291         /*
292          * close the heap relation.
293          *
294          * Currently, we do not release the AccessShareLock acquired by
295          * ExecInitTidScan.  This lock should be held till end of transaction.
296          * (There is a faction that considers this too much locking, however.)
297          */
298         heap_close(node->ss.ss_currentRelation, NoLock);
299 }
300
301 /* ----------------------------------------------------------------
302  *              ExecTidMarkPos
303  *
304  *              Marks scan position by marking the current tid.
305  *              Returns nothing.
306  * ----------------------------------------------------------------
307  */
308 void
309 ExecTidMarkPos(TidScanState *node)
310 {
311         node->tss_MarkTidPtr = node->tss_TidPtr;
312 }
313
314 /* ----------------------------------------------------------------
315  *              ExecTidRestrPos
316  *
317  *              Restores scan position by restoring the current tid.
318  *              Returns nothing.
319  *
320  *              XXX Assumes previously marked scan position belongs to current tid
321  * ----------------------------------------------------------------
322  */
323 void
324 ExecTidRestrPos(TidScanState *node)
325 {
326         node->tss_TidPtr = node->tss_MarkTidPtr;
327 }
328
329 /* ----------------------------------------------------------------
330  *              ExecInitTidScan
331  *
332  *              Initializes the tid scan's state information, creates
333  *              scan keys, and opens the base and tid relations.
334  *
335  *              Parameters:
336  *                node: TidNode node produced by the planner.
337  *                estate: the execution state initialized in InitPlan.
338  * ----------------------------------------------------------------
339  */
340 TidScanState *
341 ExecInitTidScan(TidScan *node, EState *estate)
342 {
343         TidScanState *tidstate;
344         ItemPointerData *tidList;
345         int                     numTids;
346         int                     tidPtr;
347         List       *rangeTable;
348         RangeTblEntry *rtentry;
349         Oid                     relid;
350         Oid                     reloid;
351         Relation        currentRelation;
352         List       *execParam = NIL;
353
354         /*
355          * create state structure
356          */
357         tidstate = makeNode(TidScanState);
358         tidstate->ss.ps.plan = (Plan *) node;
359         tidstate->ss.ps.state = estate;
360
361         /*
362          * Miscellaneous initialization
363          *
364          * create expression context for node
365          */
366         ExecAssignExprContext(estate, &tidstate->ss.ps);
367
368         /*
369          * initialize child expressions
370          */
371         tidstate->ss.ps.targetlist = (List *)
372                 ExecInitExpr((Expr *) node->scan.plan.targetlist,
373                                          (PlanState *) tidstate);
374         tidstate->ss.ps.qual = (List *)
375                 ExecInitExpr((Expr *) node->scan.plan.qual,
376                                          (PlanState *) tidstate);
377
378 #define TIDSCAN_NSLOTS 2
379
380         /*
381          * tuple table initialization
382          */
383         ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
384         ExecInitScanTupleSlot(estate, &tidstate->ss);
385
386         /*
387          * Initialize result tuple type and projection info.
388          */
389         ExecAssignResultTypeFromTL(&tidstate->ss.ps);
390         ExecAssignProjectionInfo(&tidstate->ss.ps);
391
392         /*
393          * get the tid node information
394          */
395         tidList = (ItemPointerData *) palloc(length(node->tideval) * sizeof(ItemPointerData));
396         tidstate->tss_tideval = (List *)
397                 ExecInitExpr((Expr *) node->tideval,
398                                          (PlanState *) tidstate);
399         numTids = TidListCreate(tidstate->tss_tideval,
400                                                         tidstate->ss.ps.ps_ExprContext,
401                                                         tidList);
402         tidPtr = -1;
403
404         CXT1_printf("ExecInitTidScan: context is %d\n", CurrentMemoryContext);
405
406         tidstate->tss_NumTids = numTids;
407         tidstate->tss_TidPtr = tidPtr;
408         tidstate->tss_TidList = tidList;
409
410         /*
411          * get the range table and direction information from the execution
412          * state (these are needed to open the relations).
413          */
414         rangeTable = estate->es_range_table;
415
416         /*
417          * open the base relation
418          *
419          * We acquire AccessShareLock for the duration of the scan.
420          */
421         relid = node->scan.scanrelid;
422         rtentry = rt_fetch(relid, rangeTable);
423         reloid = rtentry->relid;
424
425         currentRelation = heap_open(reloid, AccessShareLock);
426
427         tidstate->ss.ss_currentRelation = currentRelation;
428         tidstate->ss.ss_currentScanDesc = NULL;         /* no heap scan here */
429
430         /*
431          * get the scan type from the relation descriptor.
432          */
433         ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation), false);
434
435         /*
436          * if there are some PARAM_EXEC in skankeys then force tid rescan on
437          * first scan.
438          */
439         tidstate->ss.ps.chgParam = execParam;
440
441         /*
442          * all done.
443          */
444         return tidstate;
445 }
446
447 int
448 ExecCountSlotsTidScan(TidScan *node)
449 {
450         return ExecCountSlotsNode(outerPlan((Plan *) node)) +
451                 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
452 }