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