1
/*-------------------------------------------------------------------------
4
* Routines to support direct tid scans of relations
6
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
7
* Portions Copyright (c) 1994, Regents of the University of California
11
* src/backend/executor/nodeTidscan.c
13
*-------------------------------------------------------------------------
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.
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"
37
#define IsCTIDVar(node) \
40
((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
41
((Var *) (node))->varlevelsup == 0)
43
static void TidListCreate(TidScanState *tidstate);
44
static int itemptr_comparator(const void *a, const void *b);
45
static TupleTableSlot *TidNext(TidScanState *node);
49
* Compute the list of TIDs to be visited, by evaluating the expressions
52
* (The result is actually an array, not a list.)
55
TidListCreate(TidScanState *tidstate)
57
List *evalList = tidstate->tss_tidquals;
58
ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
60
ItemPointerData *tidList;
66
* We silently discard any TIDs that are out of range at the time of scan
67
* start. (Since we hold at least AccessShareLock on the table, it won't
68
* be possible for someone to truncate away the blocks we intend to
71
nblocks = RelationGetNumberOfBlocks(tidstate->ss.ss_currentRelation);
74
* We initialize the array with enough slots for the case that all quals
75
* are simple OpExprs or CurrentOfExprs. If there are any
76
* ScalarArrayOpExprs, we may have to enlarge the array.
78
numAllocTids = list_length(evalList);
79
tidList = (ItemPointerData *)
80
palloc(numAllocTids * sizeof(ItemPointerData));
82
tidstate->tss_isCurrentOf = false;
86
ExprState *exstate = (ExprState *) lfirst(l);
87
Expr *expr = exstate->expr;
91
if (is_opclause(expr))
93
FuncExprState *fexstate = (FuncExprState *) exstate;
97
arg1 = get_leftop(expr);
98
arg2 = get_rightop(expr);
100
exstate = (ExprState *) lsecond(fexstate->args);
101
else if (IsCTIDVar(arg2))
102
exstate = (ExprState *) linitial(fexstate->args);
104
elog(ERROR, "could not identify CTID variable");
106
itemptr = (ItemPointer)
107
DatumGetPointer(ExecEvalExprSwitchContext(exstate,
112
ItemPointerIsValid(itemptr) &&
113
ItemPointerGetBlockNumber(itemptr) < nblocks)
115
if (numTids >= numAllocTids)
118
tidList = (ItemPointerData *)
120
numAllocTids * sizeof(ItemPointerData));
122
tidList[numTids++] = *itemptr;
125
else if (expr && IsA(expr, ScalarArrayOpExpr))
127
ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
129
ArrayType *itemarray;
135
exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
136
arraydatum = ExecEvalExprSwitchContext(exstate,
142
itemarray = DatumGetArrayTypeP(arraydatum);
143
deconstruct_array(itemarray,
144
TIDOID, SizeOfIptrData, false, 's',
145
&ipdatums, &ipnulls, &ndatums);
146
if (numTids + ndatums > numAllocTids)
148
numAllocTids = numTids + ndatums;
149
tidList = (ItemPointerData *)
151
numAllocTids * sizeof(ItemPointerData));
153
for (i = 0; i < ndatums; i++)
157
itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
158
if (ItemPointerIsValid(itemptr) &&
159
ItemPointerGetBlockNumber(itemptr) < nblocks)
160
tidList[numTids++] = *itemptr;
166
else if (expr && IsA(expr, CurrentOfExpr))
168
CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
169
ItemPointerData cursor_tid;
171
if (execCurrentOf(cexpr, econtext,
172
RelationGetRelid(tidstate->ss.ss_currentRelation),
175
if (numTids >= numAllocTids)
178
tidList = (ItemPointerData *)
180
numAllocTids * sizeof(ItemPointerData));
182
tidList[numTids++] = cursor_tid;
183
tidstate->tss_isCurrentOf = true;
187
elog(ERROR, "could not identify CTID expression");
191
* Sort the array of TIDs into order, and eliminate duplicates.
192
* Eliminating duplicates is necessary since we want OR semantics across
193
* the list. Sorting makes it easier to detect duplicates, and as a bonus
194
* ensures that we will visit the heap in the most efficient way.
201
/* CurrentOfExpr could never appear OR'd with something else */
202
Assert(!tidstate->tss_isCurrentOf);
204
qsort((void *) tidList, numTids, sizeof(ItemPointerData),
207
for (i = 1; i < numTids; i++)
209
if (!ItemPointerEquals(&tidList[lastTid], &tidList[i]))
210
tidList[++lastTid] = tidList[i];
212
numTids = lastTid + 1;
215
tidstate->tss_TidList = tidList;
216
tidstate->tss_NumTids = numTids;
217
tidstate->tss_TidPtr = -1;
221
* qsort comparator for ItemPointerData items
224
itemptr_comparator(const void *a, const void *b)
226
const ItemPointerData *ipa = (const ItemPointerData *) a;
227
const ItemPointerData *ipb = (const ItemPointerData *) b;
228
BlockNumber ba = ItemPointerGetBlockNumber(ipa);
229
BlockNumber bb = ItemPointerGetBlockNumber(ipb);
230
OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
231
OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
244
/* ----------------------------------------------------------------
247
* Retrieve a tuple from the TidScan node's currentRelation
248
* using the tids in the TidScanState information.
250
* ----------------------------------------------------------------
252
static TupleTableSlot *
253
TidNext(TidScanState *node)
256
ScanDirection direction;
258
Relation heapRelation;
260
TupleTableSlot *slot;
261
Buffer buffer = InvalidBuffer;
262
ItemPointerData *tidList;
267
* extract necessary information from tid scan node
269
estate = node->ss.ps.state;
270
direction = estate->es_direction;
271
snapshot = estate->es_snapshot;
272
heapRelation = node->ss.ss_currentRelation;
273
slot = node->ss.ss_ScanTupleSlot;
276
* First time through, compute the list of TIDs to be visited
278
if (node->tss_TidList == NULL)
281
tidList = node->tss_TidList;
282
numTids = node->tss_NumTids;
284
tuple = &(node->tss_htup);
287
* Initialize or advance scan position, depending on direction.
289
bBackward = ScanDirectionIsBackward(direction);
292
if (node->tss_TidPtr < 0)
294
/* initialize for backward scan */
295
node->tss_TidPtr = numTids - 1;
302
if (node->tss_TidPtr < 0)
304
/* initialize for forward scan */
305
node->tss_TidPtr = 0;
311
while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
313
tuple->t_self = tidList[node->tss_TidPtr];
316
* For WHERE CURRENT OF, the tuple retrieved from the cursor might
317
* since have been updated; if so, we should fetch the version that is
318
* current according to our snapshot.
320
if (node->tss_isCurrentOf)
321
heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self);
323
if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL))
326
* store the scanned tuple in the scan tuple slot of the scan
327
* state. Eventually we will only do this and not return a tuple.
328
* Note: we pass 'false' because tuples returned by amgetnext are
329
* pointers onto disk pages and were not created with palloc() and
330
* so should not be pfree()'d.
332
ExecStoreTuple(tuple, /* tuple to store */
333
slot, /* slot to store in */
334
buffer, /* buffer associated with tuple */
335
false); /* don't pfree */
338
* At this point we have an extra pin on the buffer, because
339
* ExecStoreTuple incremented the pin count. Drop our local pin.
341
ReleaseBuffer(buffer);
345
/* Bad TID or failed snapshot qual; try next */
353
* if we get here it means the tid scan failed so we are at the end of the
356
return ExecClearTuple(slot);
360
* TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
363
TidRecheck(TidScanState *node, TupleTableSlot *slot)
366
* XXX shouldn't we check here to make sure tuple matches TID list? In
367
* runtime-key case this is not certain, is it? However, in the WHERE
368
* CURRENT OF case it might not match anyway ...
374
/* ----------------------------------------------------------------
377
* Scans the relation using tids and returns
378
* the next qualifying tuple in the direction specified.
379
* We call the ExecScan() routine and pass it the appropriate
380
* access method functions.
383
* -- the "cursor" maintained by the AMI is positioned at the tuple
384
* returned previously.
387
* -- the relation indicated is opened for scanning so that the
388
* "cursor" is positioned before the first qualifying tuple.
390
* ----------------------------------------------------------------
393
ExecTidScan(TidScanState *node)
395
return ExecScan(&node->ss,
396
(ExecScanAccessMtd) TidNext,
397
(ExecScanRecheckMtd) TidRecheck);
400
/* ----------------------------------------------------------------
401
* ExecReScanTidScan(node)
402
* ----------------------------------------------------------------
405
ExecReScanTidScan(TidScanState *node)
407
if (node->tss_TidList)
408
pfree(node->tss_TidList);
409
node->tss_TidList = NULL;
410
node->tss_NumTids = 0;
411
node->tss_TidPtr = -1;
413
ExecScanReScan(&node->ss);
416
/* ----------------------------------------------------------------
419
* Releases any storage allocated through C routines.
421
* ----------------------------------------------------------------
424
ExecEndTidScan(TidScanState *node)
427
* Free the exprcontext
429
ExecFreeExprContext(&node->ss.ps);
432
* clear out tuple table slots
434
ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
435
ExecClearTuple(node->ss.ss_ScanTupleSlot);
438
* close the heap relation.
440
ExecCloseScanRelation(node->ss.ss_currentRelation);
443
/* ----------------------------------------------------------------
446
* Marks scan position by marking the current tid.
448
* ----------------------------------------------------------------
451
ExecTidMarkPos(TidScanState *node)
453
node->tss_MarkTidPtr = node->tss_TidPtr;
456
/* ----------------------------------------------------------------
459
* Restores scan position by restoring the current tid.
462
* XXX Assumes previously marked scan position belongs to current tid
463
* ----------------------------------------------------------------
466
ExecTidRestrPos(TidScanState *node)
468
node->tss_TidPtr = node->tss_MarkTidPtr;
471
/* ----------------------------------------------------------------
474
* Initializes the tid scan's state information, creates
475
* scan keys, and opens the base and tid relations.
478
* node: TidNode node produced by the planner.
479
* estate: the execution state initialized in InitPlan.
480
* ----------------------------------------------------------------
483
ExecInitTidScan(TidScan *node, EState *estate, int eflags)
485
TidScanState *tidstate;
486
Relation currentRelation;
489
* create state structure
491
tidstate = makeNode(TidScanState);
492
tidstate->ss.ps.plan = (Plan *) node;
493
tidstate->ss.ps.state = estate;
496
* Miscellaneous initialization
498
* create expression context for node
500
ExecAssignExprContext(estate, &tidstate->ss.ps);
502
tidstate->ss.ps.ps_TupFromTlist = false;
505
* initialize child expressions
507
tidstate->ss.ps.targetlist = (List *)
508
ExecInitExpr((Expr *) node->scan.plan.targetlist,
509
(PlanState *) tidstate);
510
tidstate->ss.ps.qual = (List *)
511
ExecInitExpr((Expr *) node->scan.plan.qual,
512
(PlanState *) tidstate);
514
tidstate->tss_tidquals = (List *)
515
ExecInitExpr((Expr *) node->tidquals,
516
(PlanState *) tidstate);
519
* tuple table initialization
521
ExecInitResultTupleSlot(estate, &tidstate->ss.ps);
522
ExecInitScanTupleSlot(estate, &tidstate->ss);
525
* mark tid list as not computed yet
527
tidstate->tss_TidList = NULL;
528
tidstate->tss_NumTids = 0;
529
tidstate->tss_TidPtr = -1;
532
* open the base relation and acquire appropriate lock on it.
534
currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid);
536
tidstate->ss.ss_currentRelation = currentRelation;
537
tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
540
* get the scan type from the relation descriptor.
542
ExecAssignScanType(&tidstate->ss, RelationGetDescr(currentRelation));
545
* Initialize result tuple type and projection info.
547
ExecAssignResultTypeFromTL(&tidstate->ss.ps);
548
ExecAssignScanProjectionInfo(&tidstate->ss);