Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * nodeTidscan.c
4 : : * Routines to support direct tid scans of relations
5 : : *
6 : : * Portions Copyright (c) 1996-2025, 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 "access/tableam.h"
27 : : #include "catalog/pg_type.h"
28 : : #include "executor/executor.h"
29 : : #include "executor/nodeTidscan.h"
30 : : #include "lib/qunique.h"
31 : : #include "miscadmin.h"
32 : : #include "nodes/nodeFuncs.h"
33 : : #include "utils/array.h"
34 : : #include "utils/rel.h"
35 : :
36 : :
37 : : /*
38 : : * It's sufficient to check varattno to identify the CTID variable, as any
39 : : * Var in the relation scan qual must be for our table. (Even if it's a
40 : : * parameterized scan referencing some other table's CTID, the other table's
41 : : * Var would have become a Param by the time it gets here.)
42 : : */
43 : : #define IsCTIDVar(node) \
44 : : ((node) != NULL && \
45 : : IsA((node), Var) && \
46 : : ((Var *) (node))->varattno == SelfItemPointerAttributeNumber)
47 : :
48 : : /* one element in tss_tidexprs */
49 : : typedef struct TidExpr
50 : : {
51 : : ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
52 : : bool isarray; /* if true, it yields tid[] not just tid */
53 : : CurrentOfExpr *cexpr; /* alternatively, we can have CURRENT OF */
54 : : } TidExpr;
55 : :
56 : : static void TidExprListCreate(TidScanState *tidstate);
57 : : static void TidListEval(TidScanState *tidstate);
58 : : static int itemptr_comparator(const void *a, const void *b);
59 : : static TupleTableSlot *TidNext(TidScanState *node);
60 : :
61 : :
62 : : /*
63 : : * Extract the qual subexpressions that yield TIDs to search for,
64 : : * and compile them into ExprStates if they're ordinary expressions.
65 : : *
66 : : * CURRENT OF is a special case that we can't compile usefully;
67 : : * just drop it into the TidExpr list as-is.
68 : : */
69 : : static void
3098 andres@anarazel.de 70 :CBC 423 : TidExprListCreate(TidScanState *tidstate)
71 : : {
72 : 423 : TidScan *node = (TidScan *) tidstate->ss.ps.plan;
73 : : ListCell *l;
74 : :
75 : 423 : tidstate->tss_tidexprs = NIL;
76 : 423 : tidstate->tss_isCurrentOf = false;
77 : :
78 [ + - + + : 858 : foreach(l, node->tidquals)
+ + ]
79 : : {
80 : 435 : Expr *expr = (Expr *) lfirst(l);
81 : 435 : TidExpr *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
82 : :
83 [ + + ]: 435 : if (is_opclause(expr))
84 : : {
85 : : Node *arg1;
86 : : Node *arg2;
87 : :
88 : 184 : arg1 = get_leftop(expr);
89 : 184 : arg2 = get_rightop(expr);
90 [ + - + + : 184 : if (IsCTIDVar(arg1))
+ - ]
91 : 160 : tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
92 : : &tidstate->ss.ps);
93 [ + - + - : 24 : else if (IsCTIDVar(arg2))
+ - ]
94 : 24 : tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
95 : : &tidstate->ss.ps);
96 : : else
3098 andres@anarazel.de 97 [ # # ]:UBC 0 : elog(ERROR, "could not identify CTID variable");
3098 andres@anarazel.de 98 :CBC 184 : tidexpr->isarray = false;
99 : : }
100 [ + - + + ]: 251 : else if (expr && IsA(expr, ScalarArrayOpExpr))
101 : 25 : {
102 : 25 : ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
103 : :
104 [ + - + - : 25 : Assert(IsCTIDVar(linitial(saex->args)));
- + ]
105 : 25 : tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
106 : : &tidstate->ss.ps);
107 : 25 : tidexpr->isarray = true;
108 : : }
109 [ + - + - ]: 226 : else if (expr && IsA(expr, CurrentOfExpr))
110 : 226 : {
111 : 226 : CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
112 : :
113 : 226 : tidexpr->cexpr = cexpr;
114 : 226 : tidstate->tss_isCurrentOf = true;
115 : : }
116 : : else
3098 andres@anarazel.de 117 [ # # ]:UBC 0 : elog(ERROR, "could not identify CTID expression");
118 : :
3098 andres@anarazel.de 119 :CBC 435 : tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
120 : : }
121 : :
122 : : /* CurrentOfExpr could never appear OR'd with something else */
123 [ + + - + ]: 423 : Assert(list_length(tidstate->tss_tidexprs) == 1 ||
124 : : !tidstate->tss_isCurrentOf);
125 : 423 : }
126 : :
127 : : /*
128 : : * Compute the list of TIDs to be visited, by evaluating the expressions
129 : : * for them.
130 : : *
131 : : * (The result is actually an array, not a list.)
132 : : */
133 : : static void
134 : 375 : TidListEval(TidScanState *tidstate)
135 : : {
8016 tgl@sss.pgh.pa.us 136 : 375 : ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
137 : : TableScanDesc scan;
138 : : ItemPointerData *tidList;
139 : : int numAllocTids;
140 : : int numTids;
141 : : ListCell *l;
142 : :
143 : : /*
144 : : * Start scan on-demand - initializing a scan isn't free (e.g. heap stats
145 : : * the size of the table), so it makes sense to delay that until needed -
146 : : * the node might never get executed.
147 : : */
2304 andres@anarazel.de 148 [ + + ]: 375 : if (tidstate->ss.ss_currentScanDesc == NULL)
149 : 372 : tidstate->ss.ss_currentScanDesc =
2038 fujii@postgresql.org 150 : 372 : table_beginscan_tid(tidstate->ss.ss_currentRelation,
1941 tgl@sss.pgh.pa.us 151 : 372 : tidstate->ss.ps.state->es_snapshot);
2304 andres@anarazel.de 152 : 375 : scan = tidstate->ss.ss_currentScanDesc;
153 : :
154 : : /*
155 : : * We initialize the array with enough slots for the case that all quals
156 : : * are simple OpExprs or CurrentOfExprs. If there are any
157 : : * ScalarArrayOpExprs, we may have to enlarge the array.
158 : : */
3098 159 : 375 : numAllocTids = list_length(tidstate->tss_tidexprs);
160 : : tidList = (ItemPointerData *)
7224 tgl@sss.pgh.pa.us 161 : 375 : palloc(numAllocTids * sizeof(ItemPointerData));
162 : 375 : numTids = 0;
163 : :
3098 andres@anarazel.de 164 [ + - + + : 726 : foreach(l, tidstate->tss_tidexprs)
+ + ]
165 : : {
166 : 381 : TidExpr *tidexpr = (TidExpr *) lfirst(l);
167 : : ItemPointer itemptr;
168 : : bool isNull;
169 : :
170 [ + + + + ]: 381 : if (tidexpr->exprstate && !tidexpr->isarray)
171 : : {
172 : : itemptr = (ItemPointer)
173 : 163 : DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
174 : : econtext,
175 : : &isNull));
2304 176 [ - + ]: 163 : if (isNull)
2304 andres@anarazel.de 177 :UBC 0 : continue;
178 : :
179 : : /*
180 : : * We silently discard any TIDs that the AM considers invalid
181 : : * (E.g. for heap, they could be out of range at the time of scan
182 : : * start. Since we hold at least AccessShareLock on the table, it
183 : : * won't be possible for someone to truncate away the blocks we
184 : : * intend to visit.).
185 : : */
2304 andres@anarazel.de 186 [ - + ]:CBC 163 : if (!table_tuple_tid_valid(scan, itemptr))
2304 andres@anarazel.de 187 :UBC 0 : continue;
188 : :
2304 andres@anarazel.de 189 [ + + ]:CBC 163 : if (numTids >= numAllocTids)
190 : : {
191 : 3 : numAllocTids *= 2;
192 : : tidList = (ItemPointerData *)
193 : 3 : repalloc(tidList,
194 : : numAllocTids * sizeof(ItemPointerData));
195 : : }
196 : 163 : tidList[numTids++] = *itemptr;
197 : : }
3098 198 [ + + + - ]: 218 : else if (tidexpr->exprstate && tidexpr->isarray)
7224 tgl@sss.pgh.pa.us 199 : 22 : {
200 : : Datum arraydatum;
201 : : ArrayType *itemarray;
202 : : Datum *ipdatums;
203 : : bool *ipnulls;
204 : : int ndatums;
205 : : int i;
206 : :
3098 andres@anarazel.de 207 : 22 : arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
208 : : econtext,
209 : : &isNull);
7224 tgl@sss.pgh.pa.us 210 [ - + ]: 22 : if (isNull)
7224 tgl@sss.pgh.pa.us 211 :UBC 0 : continue;
7224 tgl@sss.pgh.pa.us 212 :CBC 22 : itemarray = DatumGetArrayTypeP(arraydatum);
1163 peter@eisentraut.org 213 : 22 : deconstruct_array_builtin(itemarray, TIDOID, &ipdatums, &ipnulls, &ndatums);
7224 tgl@sss.pgh.pa.us 214 [ + + ]: 22 : if (numTids + ndatums > numAllocTids)
215 : : {
216 : 19 : numAllocTids = numTids + ndatums;
217 : : tidList = (ItemPointerData *)
218 : 19 : repalloc(tidList,
219 : : numAllocTids * sizeof(ItemPointerData));
220 : : }
221 [ + + ]: 78 : for (i = 0; i < ndatums; i++)
222 : : {
2304 andres@anarazel.de 223 [ - + ]: 56 : if (ipnulls[i])
2304 andres@anarazel.de 224 :UBC 0 : continue;
225 : :
2304 andres@anarazel.de 226 :CBC 56 : itemptr = (ItemPointer) DatumGetPointer(ipdatums[i]);
227 : :
228 [ + + ]: 56 : if (!table_tuple_tid_valid(scan, itemptr))
229 : 9 : continue;
230 : :
231 : 47 : tidList[numTids++] = *itemptr;
232 : : }
7224 tgl@sss.pgh.pa.us 233 : 22 : pfree(ipdatums);
234 : 22 : pfree(ipnulls);
235 : : }
236 : : else
237 : : {
238 : : ItemPointerData cursor_tid;
239 : :
3098 andres@anarazel.de 240 [ - + ]: 196 : Assert(tidexpr->cexpr);
241 [ + + ]: 166 : if (execCurrentOf(tidexpr->cexpr, econtext,
2999 tgl@sss.pgh.pa.us 242 : 196 : RelationGetRelid(tidstate->ss.ss_currentRelation),
243 : : &cursor_tid))
244 : : {
6662 245 [ - + ]: 141 : if (numTids >= numAllocTids)
246 : : {
6662 tgl@sss.pgh.pa.us 247 :UBC 0 : numAllocTids *= 2;
248 : : tidList = (ItemPointerData *)
249 : 0 : repalloc(tidList,
250 : : numAllocTids * sizeof(ItemPointerData));
251 : : }
6662 tgl@sss.pgh.pa.us 252 :CBC 141 : tidList[numTids++] = cursor_tid;
253 : : }
254 : : }
255 : : }
256 : :
257 : : /*
258 : : * Sort the array of TIDs into order, and eliminate duplicates.
259 : : * Eliminating duplicates is necessary since we want OR semantics across
260 : : * the list. Sorting makes it easier to detect duplicates, and as a bonus
261 : : * ensures that we will visit the heap in the most efficient way.
262 : : */
7224 263 [ + + ]: 345 : if (numTids > 1)
264 : : {
265 : : /* CurrentOfExpr could never appear OR'd with something else */
6527 266 [ - + ]: 25 : Assert(!tidstate->tss_isCurrentOf);
267 : :
942 peter@eisentraut.org 268 : 25 : qsort(tidList, numTids, sizeof(ItemPointerData),
269 : : itemptr_comparator);
2130 tmunro@postgresql.or 270 : 25 : numTids = qunique(tidList, numTids, sizeof(ItemPointerData),
271 : : itemptr_comparator);
272 : : }
273 : :
8016 tgl@sss.pgh.pa.us 274 : 345 : tidstate->tss_TidList = tidList;
275 : 345 : tidstate->tss_NumTids = numTids;
276 : 345 : tidstate->tss_TidPtr = -1;
9419 bruce@momjian.us 277 : 345 : }
278 : :
279 : : /*
280 : : * qsort comparator for ItemPointerData items
281 : : */
282 : : static int
7224 tgl@sss.pgh.pa.us 283 : 65 : itemptr_comparator(const void *a, const void *b)
284 : : {
285 : 65 : const ItemPointerData *ipa = (const ItemPointerData *) a;
286 : 65 : const ItemPointerData *ipb = (const ItemPointerData *) b;
6912 bruce@momjian.us 287 : 65 : BlockNumber ba = ItemPointerGetBlockNumber(ipa);
288 : 65 : BlockNumber bb = ItemPointerGetBlockNumber(ipb);
7224 tgl@sss.pgh.pa.us 289 : 65 : OffsetNumber oa = ItemPointerGetOffsetNumber(ipa);
290 : 65 : OffsetNumber ob = ItemPointerGetOffsetNumber(ipb);
291 : :
292 [ + + ]: 65 : if (ba < bb)
293 : 4 : return -1;
294 [ + + ]: 61 : if (ba > bb)
295 : 4 : return 1;
296 [ + + ]: 57 : if (oa < ob)
297 : 21 : return -1;
298 [ + - ]: 36 : if (oa > ob)
299 : 36 : return 1;
7224 tgl@sss.pgh.pa.us 300 :UBC 0 : return 0;
301 : : }
302 : :
303 : : /* ----------------------------------------------------------------
304 : : * TidNext
305 : : *
306 : : * Retrieve a tuple from the TidScan node's currentRelation
307 : : * using the tids in the TidScanState information.
308 : : *
309 : : * ----------------------------------------------------------------
310 : : */
311 : : static TupleTableSlot *
8311 tgl@sss.pgh.pa.us 312 :CBC 687 : TidNext(TidScanState *node)
313 : : {
314 : : EState *estate;
315 : : ScanDirection direction;
316 : : Snapshot snapshot;
317 : : TableScanDesc scan;
318 : : Relation heapRelation;
319 : : TupleTableSlot *slot;
320 : : ItemPointerData *tidList;
321 : : int numTids;
322 : : bool bBackward;
323 : :
324 : : /*
325 : : * extract necessary information from tid scan node
326 : : */
327 : 687 : estate = node->ss.ps.state;
9419 bruce@momjian.us 328 : 687 : direction = estate->es_direction;
329 : 687 : snapshot = estate->es_snapshot;
8311 tgl@sss.pgh.pa.us 330 : 687 : heapRelation = node->ss.ss_currentRelation;
331 : 687 : slot = node->ss.ss_ScanTupleSlot;
332 : :
333 : : /*
334 : : * First time through, compute the list of TIDs to be visited
335 : : */
8016 336 [ + + ]: 687 : if (node->tss_TidList == NULL)
3098 andres@anarazel.de 337 : 375 : TidListEval(node);
338 : :
2304 339 : 657 : scan = node->ss.ss_currentScanDesc;
8016 tgl@sss.pgh.pa.us 340 : 657 : tidList = node->tss_TidList;
341 : 657 : numTids = node->tss_NumTids;
342 : :
343 : : /*
344 : : * Initialize or advance scan position, depending on direction.
345 : : */
9419 bruce@momjian.us 346 : 657 : bBackward = ScanDirectionIsBackward(direction);
347 [ + + ]: 657 : if (bBackward)
348 : : {
7224 tgl@sss.pgh.pa.us 349 [ - + ]: 3 : if (node->tss_TidPtr < 0)
350 : : {
351 : : /* initialize for backward scan */
8311 tgl@sss.pgh.pa.us 352 :UBC 0 : node->tss_TidPtr = numTids - 1;
353 : : }
354 : : else
7224 tgl@sss.pgh.pa.us 355 :CBC 3 : node->tss_TidPtr--;
356 : : }
357 : : else
358 : : {
359 [ + + ]: 654 : if (node->tss_TidPtr < 0)
360 : : {
361 : : /* initialize for forward scan */
8311 362 : 345 : node->tss_TidPtr = 0;
363 : : }
364 : : else
7224 365 : 309 : node->tss_TidPtr++;
366 : : }
367 : :
368 [ + - + + ]: 678 : while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids)
369 : : {
2357 andres@anarazel.de 370 : 348 : ItemPointerData tid = tidList[node->tss_TidPtr];
371 : :
372 : : /*
373 : : * For WHERE CURRENT OF, the tuple retrieved from the cursor might
374 : : * since have been updated; if so, we should fetch the version that is
375 : : * current according to our snapshot.
376 : : */
6527 tgl@sss.pgh.pa.us 377 [ + + ]: 348 : if (node->tss_isCurrentOf)
2298 andres@anarazel.de 378 : 141 : table_tuple_get_latest_tid(scan, &tid);
379 : :
380 [ + + ]: 348 : if (table_tuple_fetch_row_version(heapRelation, &tid, snapshot, slot))
7224 tgl@sss.pgh.pa.us 381 : 318 : return slot;
382 : :
383 : : /* Bad TID or failed snapshot qual; try next */
9419 bruce@momjian.us 384 [ - + ]: 21 : if (bBackward)
8311 tgl@sss.pgh.pa.us 385 :UBC 0 : node->tss_TidPtr--;
386 : : else
8311 tgl@sss.pgh.pa.us 387 :CBC 21 : node->tss_TidPtr++;
388 : :
2965 andres@anarazel.de 389 [ - + ]: 21 : CHECK_FOR_INTERRUPTS();
390 : : }
391 : :
392 : : /*
393 : : * if we get here it means the tid scan failed so we are at the end of the
394 : : * scan..
395 : : */
9419 bruce@momjian.us 396 : 330 : return ExecClearTuple(slot);
397 : : }
398 : :
399 : : /*
400 : : * TidRecheck -- access method routine to recheck a tuple in EvalPlanQual
401 : : */
402 : : static bool
5794 tgl@sss.pgh.pa.us 403 :UBC 0 : TidRecheck(TidScanState *node, TupleTableSlot *slot)
404 : : {
405 : : /*
406 : : * XXX shouldn't we check here to make sure tuple matches TID list? In
407 : : * runtime-key case this is not certain, is it? However, in the WHERE
408 : : * CURRENT OF case it might not match anyway ...
409 : : */
410 : 0 : return true;
411 : : }
412 : :
413 : :
414 : : /* ----------------------------------------------------------------
415 : : * ExecTidScan(node)
416 : : *
417 : : * Scans the relation using tids and returns
418 : : * the next qualifying tuple in the direction specified.
419 : : * We call the ExecScan() routine and pass it the appropriate
420 : : * access method functions.
421 : : *
422 : : * Conditions:
423 : : * -- the "cursor" maintained by the AMI is positioned at the tuple
424 : : * returned previously.
425 : : *
426 : : * Initial States:
427 : : * -- the relation indicated is opened for scanning so that the
428 : : * "cursor" is positioned before the first qualifying tuple.
429 : : * -- tss_TidPtr is -1.
430 : : * ----------------------------------------------------------------
431 : : */
432 : : static TupleTableSlot *
2973 andres@anarazel.de 433 :CBC 678 : ExecTidScan(PlanState *pstate)
434 : : {
435 : 678 : TidScanState *node = castNode(TidScanState, pstate);
436 : :
5794 tgl@sss.pgh.pa.us 437 : 678 : return ExecScan(&node->ss,
438 : : (ExecScanAccessMtd) TidNext,
439 : : (ExecScanRecheckMtd) TidRecheck);
440 : : }
441 : :
442 : : /* ----------------------------------------------------------------
443 : : * ExecReScanTidScan(node)
444 : : * ----------------------------------------------------------------
445 : : */
446 : : void
5535 447 : 9 : ExecReScanTidScan(TidScanState *node)
448 : : {
8016 449 [ + + ]: 9 : if (node->tss_TidList)
450 : 3 : pfree(node->tss_TidList);
451 : 9 : node->tss_TidList = NULL;
452 : 9 : node->tss_NumTids = 0;
8311 453 : 9 : node->tss_TidPtr = -1;
454 : :
455 : : /* not really necessary, but seems good form */
2304 andres@anarazel.de 456 [ + + ]: 9 : if (node->ss.ss_currentScanDesc)
457 : 3 : table_rescan(node->ss.ss_currentScanDesc, NULL);
458 : :
5794 tgl@sss.pgh.pa.us 459 : 9 : ExecScanReScan(&node->ss);
9419 bruce@momjian.us 460 : 9 : }
461 : :
462 : : /* ----------------------------------------------------------------
463 : : * ExecEndTidScan
464 : : *
465 : : * Releases any storage allocated through C routines.
466 : : * Returns nothing.
467 : : * ----------------------------------------------------------------
468 : : */
469 : : void
8311 tgl@sss.pgh.pa.us 470 : 351 : ExecEndTidScan(TidScanState *node)
471 : : {
2304 andres@anarazel.de 472 [ + + ]: 351 : if (node->ss.ss_currentScanDesc)
473 : 324 : table_endscan(node->ss.ss_currentScanDesc);
9419 bruce@momjian.us 474 : 351 : }
475 : :
476 : : /* ----------------------------------------------------------------
477 : : * ExecInitTidScan
478 : : *
479 : : * Initializes the tid scan's state information, creates
480 : : * scan keys, and opens the base and tid relations.
481 : : *
482 : : * Parameters:
483 : : * node: TidScan node produced by the planner.
484 : : * estate: the execution state initialized in InitPlan.
485 : : * ----------------------------------------------------------------
486 : : */
487 : : TidScanState *
7130 tgl@sss.pgh.pa.us 488 : 423 : ExecInitTidScan(TidScan *node, EState *estate, int eflags)
489 : : {
490 : : TidScanState *tidstate;
491 : : Relation currentRelation;
492 : :
493 : : /*
494 : : * create state structure
495 : : */
8311 496 : 423 : tidstate = makeNode(TidScanState);
497 : 423 : tidstate->ss.ps.plan = (Plan *) node;
498 : 423 : tidstate->ss.ps.state = estate;
2973 andres@anarazel.de 499 : 423 : tidstate->ss.ps.ExecProcNode = ExecTidScan;
500 : :
501 : : /*
502 : : * Miscellaneous initialization
503 : : *
504 : : * create expression context for node
505 : : */
8311 tgl@sss.pgh.pa.us 506 : 423 : ExecAssignExprContext(estate, &tidstate->ss.ps);
507 : :
508 : : /*
509 : : * mark tid list as not computed yet
510 : : */
8016 511 : 423 : tidstate->tss_TidList = NULL;
512 : 423 : tidstate->tss_NumTids = 0;
513 : 423 : tidstate->tss_TidPtr = -1;
514 : :
515 : : /*
516 : : * open the scan relation
517 : : */
4515 518 : 423 : currentRelation = ExecOpenScanRelation(estate, node->scan.scanrelid, eflags);
519 : :
8311 520 : 423 : tidstate->ss.ss_currentRelation = currentRelation;
2999 521 : 423 : tidstate->ss.ss_currentScanDesc = NULL; /* no heap scan here */
522 : :
523 : : /*
524 : : * get the scan type from the relation descriptor.
525 : : */
2759 andres@anarazel.de 526 : 423 : ExecInitScanTupleSlot(estate, &tidstate->ss,
527 : : RelationGetDescr(currentRelation),
528 : : table_slot_callbacks(currentRelation));
529 : :
530 : : /*
531 : : * Initialize result type and projection.
532 : : */
2493 533 : 423 : ExecInitResultTypeTL(&tidstate->ss.ps);
8251 tgl@sss.pgh.pa.us 534 : 423 : ExecAssignScanProjectionInfo(&tidstate->ss);
535 : :
536 : : /*
537 : : * initialize child expressions
538 : : */
2759 andres@anarazel.de 539 : 423 : tidstate->ss.ps.qual =
540 : 423 : ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
541 : :
542 : 423 : TidExprListCreate(tidstate);
543 : :
544 : : /*
545 : : * all done.
546 : : */
8311 tgl@sss.pgh.pa.us 547 : 423 : return tidstate;
548 : : }
|