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