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