]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeTidscan.c
Update CVS HEAD for 2007 copyright. Back branches are typically not
[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-2007, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $PostgreSQL: pgsql/src/backend/executor/nodeTidscan.c,v 1.53 2007/01/05 22:19:28 momjian 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 "access/heapam.h"
28 #include "catalog/pg_type.h"
29 #include "executor/execdebug.h"
30 #include "executor/nodeTidscan.h"
31 #include "optimizer/clauses.h"
32 #include "utils/array.h"
33
34
35 #define IsCTIDVar(node)  \
36         ((node) != NULL && \
37          IsA((node), Var) && \
38          ((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
39          ((Var *) (node))->varlevelsup == 0)
40
41 static void TidListCreate(TidScanState *tidstate);
42 static int      itemptr_comparator(const void *a, const void *b);
43 static TupleTableSlot *TidNext(TidScanState *node);
44
45
46 /*
47  * Compute the list of TIDs to be visited, by evaluating the expressions
48  * for them.
49  *
50  * (The result is actually an array, not a list.)
51  */
52 static void
53 TidListCreate(TidScanState *tidstate)
54 {
55         List       *evalList = tidstate->tss_tidquals;
56         ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
57         ItemPointerData *tidList;
58         int                     numAllocTids;
59         int                     numTids;
60         ListCell   *l;
61
62         /*
63          * We initialize the array with enough slots for the case that all quals
64          * are simple OpExprs.  If there's any ScalarArrayOpExprs, we may have to
65          * enlarge the array.
66          */
67         numAllocTids = list_length(evalList);
68         tidList = (ItemPointerData *)
69                 palloc(numAllocTids * sizeof(ItemPointerData));
70         numTids = 0;
71
72         foreach(l, evalList)
73         {
74                 ExprState  *exstate = (ExprState *) lfirst(l);
75                 Expr       *expr = exstate->expr;
76                 ItemPointer itemptr;
77                 bool            isNull;
78
79                 if (is_opclause(expr))
80                 {
81                         FuncExprState *fexstate = (FuncExprState *) exstate;
82                         Node       *arg1;
83                         Node       *arg2;
84
85                         arg1 = get_leftop(expr);
86                         arg2 = get_rightop(expr);
87                         if (IsCTIDVar(arg1))
88                                 exstate = (ExprState *) lsecond(fexstate->args);
89                         else if (IsCTIDVar(arg2))
90                                 exstate = (ExprState *) linitial(fexstate->args);
91                         else
92                                 elog(ERROR, "could not identify CTID variable");
93
94                         itemptr = (ItemPointer)
95                                 DatumGetPointer(ExecEvalExprSwitchContext(exstate,
96                                                                                                                   econtext,
97                                                                                                                   &isNull,
98                                                                                                                   NULL));
99                         if (!isNull && ItemPointerIsValid(itemptr))
100                         {
101                                 if (numTids >= numAllocTids)
102                                 {
103                                         numAllocTids *= 2;
104                                         tidList = (ItemPointerData *)
105                                                 repalloc(tidList,
106                                                                  numAllocTids * sizeof(ItemPointerData));
107                                 }
108                                 tidList[numTids++] = *itemptr;
109                         }
110                 }
111                 else if (expr && IsA(expr, ScalarArrayOpExpr))
112                 {
113                         ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
114                         Datum           arraydatum;
115                         ArrayType  *itemarray;
116                         Datum      *ipdatums;
117                         bool       *ipnulls;
118                         int                     ndatums;
119                         int                     i;
120
121                         exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
122                         arraydatum = ExecEvalExprSwitchContext(exstate,
123                                                                                                    econtext,
124                                                                                                    &isNull,
125                                                                                                    NULL);
126                         if (isNull)
127                                 continue;
128                         itemarray = DatumGetArrayTypeP(arraydatum);
129                         deconstruct_array(itemarray,
130                                                           TIDOID, SizeOfIptrData, false, 's',
131                                                           &ipdatums, &ipnulls, &ndatums);
132                         if (numTids + ndatums > numAllocTids)
133                         {
134                                 numAllocTids = numTids + ndatums;
135                                 tidList = (ItemPointerData *)
136                                         repalloc(tidList,
137                                                          numAllocTids * sizeof(ItemPointerData));
138                         }
139                         for (i = 0; i < ndatums; i++)
140                         {
141                                 if (!ipnulls[i])
142                                 {
143                                         itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
144                                         if (ItemPointerIsValid(itemptr))
145                                                 tidList[numTids++] = *itemptr;
146                                 }
147                         }
148                         pfree(ipdatums);
149                         pfree(ipnulls);
150                 }
151                 else
152                         elog(ERROR, "could not identify CTID expression");
153         }
154
155         /*
156          * Sort the array of TIDs into order, and eliminate duplicates.
157          * Eliminating duplicates is necessary since we want OR semantics across
158          * the list.  Sorting makes it easier to detect duplicates, and as a bonus
159          * ensures that we will visit the heap in the most efficient way.
160          */
161         if (numTids > 1)
162         {
163                 int                     lastTid;
164                 int                     i;
165
166                 qsort((void *) tidList, numTids, sizeof(ItemPointerData),
167                           itemptr_comparator);
168                 lastTid = 0;
169                 for (i = 1; i < numTids; i++)
170                 {
171                         if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
172                                 tidList[++lastTid] = tidList[i];
173                 }
174                 numTids = lastTid + 1;
175         }
176
177         tidstate->tss_TidList = tidList;
178         tidstate->tss_NumTids = numTids;
179         tidstate->tss_TidPtr = -1;
180 }
181
182 /*
183  * qsort comparator for ItemPointerData items
184  */
185 static int
186 itemptr_comparator(const void *a, const void *b)
187 {
188         const ItemPointerData *ipa = (const ItemPointerData *) a;
189         const ItemPointerData *ipb = (const ItemPointerData *) b;
190         BlockNumber ba = ItemPointerGetBlockNumber(ipa);
191         BlockNumber bb = ItemPointerGetBlockNumber(ipb);
192         OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
193         OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
194
195         if (ba < bb)
196                 return -1;
197         if (ba > bb)
198                 return 1;
199         if (oa < ob)
200                 return -1;
201         if (oa > ob)
202                 return 1;
203         return 0;
204 }
205
206 /* ----------------------------------------------------------------
207  *              TidNext
208  *
209  *              Retrieve a tuple from the TidScan node's currentRelation
210  *              using the tids in the TidScanState information.
211  *
212  * ----------------------------------------------------------------
213  */
214 static TupleTableSlot *
215 TidNext(TidScanState *node)
216 {
217         EState     *estate;
218         ScanDirection direction;
219         Snapshot        snapshot;
220         Relation        heapRelation;
221         HeapTuple       tuple;
222         TupleTableSlot *slot;
223         Index           scanrelid;
224         Buffer          buffer = InvalidBuffer;
225         ItemPointerData *tidList;
226         int                     numTids;
227         bool            bBackward;
228
229         /*
230          * extract necessary information from tid scan node
231          */
232         estate = node->ss.ps.state;
233         direction = estate->es_direction;
234         snapshot = estate->es_snapshot;
235         heapRelation = node->ss.ss_currentRelation;
236         slot = node->ss.ss_ScanTupleSlot;
237         scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
238
239         /*
240          * Check if we are evaluating PlanQual for tuple of this relation.
241          * Additional checking is not good, but no other way for now. We could
242          * introduce new nodes for this case and handle TidScan --> NewNode
243          * switching in Init/ReScan plan...
244          */
245         if (estate->es_evTuple != NULL &&
246                 estate->es_evTuple[scanrelid - 1] != NULL)
247         {
248                 if (estate->es_evTupleNull[scanrelid - 1])
249                         return ExecClearTuple(slot);
250
251                 /*
252                  * XXX shouldn't we check here to make sure tuple matches TID list? In
253                  * runtime-key case this is not certain, is it?
254                  */
255
256                 ExecStoreTuple(estate->es_evTuple[scanrelid - 1],
257                                            slot, InvalidBuffer, false);
258
259                 /* Flag for the next call that no more tuples */
260                 estate->es_evTupleNull[scanrelid - 1] = true;
261                 return slot;
262         }
263
264         /*
265          * First time through, compute the list of TIDs to be visited
266          */
267         if (node->tss_TidList == NULL)
268                 TidListCreate(node);
269
270         tidList = node->tss_TidList;
271         numTids = node->tss_NumTids;
272
273         tuple = &(node->tss_htup);
274
275         /*
276          * Initialize or advance scan position, depending on direction.
277          */
278         bBackward = ScanDirectionIsBackward(direction);
279         if (bBackward)
280         {
281                 if (node->tss_TidPtr < 0)
282                 {
283                         /* initialize for backward scan */
284                         node->tss_TidPtr = numTids - 1;
285                 }
286                 else
287                         node->tss_TidPtr--;
288         }
289         else
290         {
291                 if (node->tss_TidPtr < 0)
292                 {
293                         /* initialize for forward scan */
294                         node->tss_TidPtr = 0;
295                 }
296                 else
297                         node->tss_TidPtr++;
298         }
299
300         while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
301         {
302                 tuple->t_self = tidList[node->tss_TidPtr];
303                 if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
304                 {
305                         /*
306                          * store the scanned tuple in the scan tuple slot of the scan
307                          * state.  Eventually we will only do this and not return a tuple.
308                          * Note: we pass 'false' because tuples returned by amgetnext are
309                          * pointers onto disk pages and were not created with palloc() and
310                          * so should not be pfree()'d.
311                          */
312                         ExecStoreTuple(tuple,           /* tuple to store */
313                                                    slot,        /* slot to store in */
314                                                    buffer,              /* buffer associated with tuple  */
315                                                    false);              /* don't pfree */
316
317                         /*
318                          * At this point we have an extra pin on the buffer, because
319                          * ExecStoreTuple incremented the pin count. Drop our local pin.
320                          */
321                         ReleaseBuffer(buffer);
322
323                         return slot;
324                 }
325                 /* Bad TID or failed snapshot qual; try next */
326                 if (bBackward)
327                         node->tss_TidPtr--;
328                 else
329                         node->tss_TidPtr++;
330         }
331
332         /*
333          * if we get here it means the tid scan failed so we are at the end of the
334          * scan..
335          */
336         return ExecClearTuple(slot);
337 }
338
339 /* ----------------------------------------------------------------
340  *              ExecTidScan(node)
341  *
342  *              Scans the relation using tids and returns
343  *                 the next qualifying tuple in the direction specified.
344  *              It calls ExecScan() and passes it the access methods which returns
345  *              the next tuple using the tids.
346  *
347  *              Conditions:
348  *                -- the "cursor" maintained by the AMI is positioned at the tuple
349  *                       returned previously.
350  *
351  *              Initial States:
352  *                -- the relation indicated is opened for scanning so that the
353  *                       "cursor" is positioned before the first qualifying tuple.
354  *                -- tidPtr is -1.
355  * ----------------------------------------------------------------
356  */
357 TupleTableSlot *
358 ExecTidScan(TidScanState *node)
359 {
360         /*
361          * use TidNext as access method
362          */
363         return ExecScan(&node->ss, (ExecScanAccessMtd) TidNext);
364 }
365
366 /* ----------------------------------------------------------------
367  *              ExecTidReScan(node)
368  * ----------------------------------------------------------------
369  */
370 void
371 ExecTidReScan(TidScanState *node, ExprContext *exprCtxt)
372 {
373         EState     *estate;
374         Index           scanrelid;
375
376         estate = node->ss.ps.state;
377         scanrelid = ((TidScan *) node->ss.ps.plan)->scan.scanrelid;
378
379         node->ss.ps.ps_TupFromTlist = false;
380
381         /* If we are being passed an outer tuple, save it for runtime key calc */
382         if (exprCtxt != NULL)
383                 node->ss.ps.ps_ExprContext->ecxt_outertuple =
384                         exprCtxt->ecxt_outertuple;
385
386         /* If this is re-scanning of PlanQual ... */
387         if (estate->es_evTuple != NULL &&
388                 estate->es_evTuple[scanrelid - 1] != NULL)
389         {
390                 estate->es_evTupleNull[scanrelid - 1] = false;
391                 return;
392         }
393
394         if (node->tss_TidList)
395                 pfree(node->tss_TidList);
396         node->tss_TidList = NULL;
397         node->tss_NumTids = 0;
398         node->tss_TidPtr = -1;
399 }
400
401 /* ----------------------------------------------------------------
402  *              ExecEndTidScan
403  *
404  *              Releases any storage allocated through C routines.
405  *              Returns nothing.
406  * ----------------------------------------------------------------
407  */
408 void
409 ExecEndTidScan(TidScanState *node)
410 {
411         /*
412          * Free the exprcontext
413          */
414         ExecFreeExprContext(&node->ss.ps);
415
416         /*
417          * clear out tuple table slots
418          */
419         ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
420         ExecClearTuple(node->ss.ss_ScanTupleSlot);
421
422         /*
423          * close the heap relation.
424          */
425         ExecCloseScanRelation(node->ss.ss_currentRelation);
426 }
427
428 /* ----------------------------------------------------------------
429  *              ExecTidMarkPos
430  *
431  *              Marks scan position by marking the current tid.
432  *              Returns nothing.
433  * ----------------------------------------------------------------
434  */
435 void
436 ExecTidMarkPos(TidScanState *node)
437 {
438         node->tss_MarkTidPtr = node->tss_TidPtr;
439 }
440
441 /* ----------------------------------------------------------------
442  *              ExecTidRestrPos
443  *
444  *              Restores scan position by restoring the current tid.
445  *              Returns nothing.
446  *
447  *              XXX Assumes previously marked scan position belongs to current tid
448  * ----------------------------------------------------------------
449  */
450 void
451 ExecTidRestrPos(TidScanState *node)
452 {
453         node->tss_TidPtr = node->tss_MarkTidPtr;
454 }
455
456 /* ----------------------------------------------------------------
457  *              ExecInitTidScan
458  *
459  *              Initializes the tid scan's state information, creates
460  *              scan keys, and opens the base and tid relations.
461  *
462  *              Parameters:
463  *                node: TidNode node produced by the planner.
464  *                estate: the execution state initialized in InitPlan.
465  * ----------------------------------------------------------------
466  */
467 TidScanState *
468 ExecInitTidScan(TidScan *node, EState *estate, int eflags)
469 {
470         TidScanState *tidstate;
471         Relation        currentRelation;
472
473         /*
474          * create state structure
475          */
476         tidstate = makeNode(TidScanState);
477         tidstate->ss.ps.plan = (Plan *) node;
478         tidstate->ss.ps.state = estate;
479
480         /*
481          * Miscellaneous initialization
482          *
483          * create expression context for node
484          */
485         ExecAssignExprContext(estate, &tidstate->ss.ps);
486
487         tidstate->ss.ps.ps_TupFromTlist = false;
488
489         /*
490          * initialize child expressions
491          */
492         tidstate->ss.ps.targetlist = (List *)
493                 ExecInitExpr((Expr *) node->scan.plan.targetlist,
494                                          (PlanState *) tidstate);
495         tidstate->ss.ps.qual = (List *)
496                 ExecInitExpr((Expr *) node->scan.plan.qual,
497                                          (PlanState *) tidstate);
498
499         tidstate->tss_tidquals = (List *)
500                 ExecInitExpr((Expr *) node->tidquals,
501                                          (PlanState *) tidstate);
502
503 #define TIDSCAN_NSLOTS 2
504
505         /*
506          * tuple table initialization
507          */
508         ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
509         ExecInitScanTupleSlot(estate, &tidstate->ss);
510
511         /*
512          * mark tid list as not computed yet
513          */
514         tidstate->tss_TidList = NULL;
515         tidstate->tss_NumTids = 0;
516         tidstate->tss_TidPtr = -1;
517
518         /*
519          * open the base relation and acquire appropriate lock on it.
520          */
521         currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
522
523         tidstate->ss.ss_currentRelation = currentRelation;
524         tidstate->ss.ss_currentScanDesc = NULL;         /* no heap scan here */
525
526         /*
527          * get the scan type from the relation descriptor.
528          */
529         ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation));
530
531         /*
532          * Initialize result tuple type and projection info.
533          */
534         ExecAssignResultTypeFromTL(&tidstate->ss.ps);
535         ExecAssignScanProjectionInfo(&tidstate->ss);
536
537         /*
538          * all done.
539          */
540         return tidstate;
541 }
542
543 int
544 ExecCountSlotsTidScan(TidScan *node)
545 {
546         return ExecCountSlotsNode(outerPlan((Plan *) node)) +
547                 ExecCountSlotsNode(innerPlan((Plan *) node)) + TIDSCAN_NSLOTS;
548 }