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