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