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