Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * execAmi.c
4 : : * miscellaneous executor access method routines
5 : : *
6 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 : : * Portions Copyright (c) 1994, Regents of the University of California
8 : : *
9 : : * src/backend/executor/execAmi.c
10 : : *
11 : : *-------------------------------------------------------------------------
12 : : */
13 : : #include "postgres.h"
14 : :
15 : : #include "access/amapi.h"
16 : : #include "access/htup_details.h"
17 : : #include "catalog/pg_class.h"
18 : : #include "executor/executor.h"
19 : : #include "executor/instrument.h"
20 : : #include "executor/nodeAgg.h"
21 : : #include "executor/nodeAppend.h"
22 : : #include "executor/nodeBitmapAnd.h"
23 : : #include "executor/nodeBitmapHeapscan.h"
24 : : #include "executor/nodeBitmapIndexscan.h"
25 : : #include "executor/nodeBitmapOr.h"
26 : : #include "executor/nodeCtescan.h"
27 : : #include "executor/nodeCustom.h"
28 : : #include "executor/nodeForeignscan.h"
29 : : #include "executor/nodeFunctionscan.h"
30 : : #include "executor/nodeGather.h"
31 : : #include "executor/nodeGatherMerge.h"
32 : : #include "executor/nodeGroup.h"
33 : : #include "executor/nodeHash.h"
34 : : #include "executor/nodeHashjoin.h"
35 : : #include "executor/nodeIncrementalSort.h"
36 : : #include "executor/nodeIndexonlyscan.h"
37 : : #include "executor/nodeIndexscan.h"
38 : : #include "executor/nodeLimit.h"
39 : : #include "executor/nodeLockRows.h"
40 : : #include "executor/nodeMaterial.h"
41 : : #include "executor/nodeMemoize.h"
42 : : #include "executor/nodeMergeAppend.h"
43 : : #include "executor/nodeMergejoin.h"
44 : : #include "executor/nodeModifyTable.h"
45 : : #include "executor/nodeNamedtuplestorescan.h"
46 : : #include "executor/nodeNestloop.h"
47 : : #include "executor/nodeProjectSet.h"
48 : : #include "executor/nodeRecursiveunion.h"
49 : : #include "executor/nodeResult.h"
50 : : #include "executor/nodeSamplescan.h"
51 : : #include "executor/nodeSeqscan.h"
52 : : #include "executor/nodeSetOp.h"
53 : : #include "executor/nodeSort.h"
54 : : #include "executor/nodeSubplan.h"
55 : : #include "executor/nodeSubqueryscan.h"
56 : : #include "executor/nodeTableFuncscan.h"
57 : : #include "executor/nodeTidrangescan.h"
58 : : #include "executor/nodeTidscan.h"
59 : : #include "executor/nodeUnique.h"
60 : : #include "executor/nodeValuesscan.h"
61 : : #include "executor/nodeWindowAgg.h"
62 : : #include "executor/nodeWorktablescan.h"
63 : : #include "nodes/extensible.h"
64 : : #include "nodes/pathnodes.h"
65 : : #include "utils/syscache.h"
66 : :
67 : : static bool IndexSupportsBackwardScan(Oid indexid);
68 : :
69 : :
70 : : /*
71 : : * ExecReScan
72 : : * Reset a plan node so that its output can be re-scanned.
73 : : *
74 : : * Note that if the plan node has parameters that have changed value,
75 : : * the output might be different from last time.
76 : : */
77 : : void
5960 tgl@sss.pgh.pa.us 78 :CBC 2576989 : ExecReScan(PlanState *node)
79 : : {
80 : : /* If collecting timing stats, update them */
9179 81 [ + + ]: 2576989 : if (node->instrument)
82 : 28583 : InstrEndLoop(node->instrument);
83 : :
84 : : /*
85 : : * If we have changed parameters, propagate that info.
86 : : *
87 : : * Note: ExecReScanSetParamPlan() can add bits to node->chgParam,
88 : : * corresponding to the output param(s) that the InitPlan will update.
89 : : * Since we make only one pass over the list, that means that an InitPlan
90 : : * can depend on the output param(s) of a sibling InitPlan only if that
91 : : * sibling appears earlier in the list. This is workable for now given
92 : : * the limited ways in which one InitPlan could depend on another, but
93 : : * eventually we might need to work harder (or else make the planner
94 : : * enlarge the extParam/allParam sets to include the params of depended-on
95 : : * InitPlans).
96 : : */
8670 97 [ + + ]: 2576989 : if (node->chgParam != NULL)
98 : : {
99 : : ListCell *l;
100 : :
8198 neilc@samurai.com 101 [ + + + + : 2331920 : foreach(l, node->initPlan)
+ + ]
102 : : {
103 : 1331 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8728 tgl@sss.pgh.pa.us 104 : 1331 : PlanState *splan = sstate->planstate;
105 : :
8670 106 [ + + ]: 1331 : if (splan->plan->extParam != NULL) /* don't care about child
107 : : * local Params */
108 : 1174 : UpdateChangedParamSet(splan, node->chgParam);
109 [ + + ]: 1331 : if (splan->chgParam != NULL)
8728 110 : 1014 : ExecReScanSetParamPlan(sstate, node);
111 : : }
8198 neilc@samurai.com 112 [ + + + + : 2371598 : foreach(l, node->subPlan)
+ + ]
113 : : {
114 : 41009 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8728 tgl@sss.pgh.pa.us 115 : 41009 : PlanState *splan = sstate->planstate;
116 : :
8670 117 [ + + ]: 41009 : if (splan->plan->extParam != NULL)
118 : 785 : UpdateChangedParamSet(splan, node->chgParam);
119 : :
120 : : /*
121 : : * Mark the hash table stale. This is tracked per SubPlanState,
122 : : * since several of them can share one subplan PlanState.
123 : : */
3 rguo@postgresql.org 124 [ + + ]: 41009 : if (splan->chgParam != NULL)
125 : 480 : sstate->hashtablestale = true;
126 : : }
127 : : /* Well. Now set chgParam for child trees. */
1582 tgl@sss.pgh.pa.us 128 [ + + ]: 2330589 : if (outerPlanState(node) != NULL)
129 : 1038062 : UpdateChangedParamSet(outerPlanState(node), node->chgParam);
130 [ + + ]: 2330589 : if (innerPlanState(node) != NULL)
131 : 52710 : UpdateChangedParamSet(innerPlanState(node), node->chgParam);
132 : : }
133 : :
134 : : /* Call expression callbacks */
8358 135 [ + + ]: 2576989 : if (node->ps_ExprContext)
136 : 2395667 : ReScanExprContext(node->ps_ExprContext);
137 : :
138 : : /* And do node-type-specific processing */
10651 bruce@momjian.us 139 [ + + - + : 2576989 : switch (nodeTag(node))
+ + + + +
+ + + + +
+ + + + +
+ + + + -
+ + - + +
+ + + + +
+ + + + +
+ + + - ]
140 : : {
8736 tgl@sss.pgh.pa.us 141 : 108566 : case T_ResultState:
5960 142 : 108566 : ExecReScanResult((ResultState *) node);
10492 vadim4o@yahoo.com 143 : 108566 : break;
144 : :
3578 andres@anarazel.de 145 : 9604 : case T_ProjectSetState:
146 : 9604 : ExecReScanProjectSet((ProjectSetState *) node);
147 : 9604 : break;
148 : :
6235 tgl@sss.pgh.pa.us 149 :UBC 0 : case T_ModifyTableState:
5960 150 : 0 : ExecReScanModifyTable((ModifyTableState *) node);
6235 151 : 0 : break;
152 : :
8736 tgl@sss.pgh.pa.us 153 :CBC 12481 : case T_AppendState:
5960 154 : 12481 : ExecReScanAppend((AppendState *) node);
10492 vadim4o@yahoo.com 155 : 12481 : break;
156 : :
5866 tgl@sss.pgh.pa.us 157 : 12 : case T_MergeAppendState:
158 : 12 : ExecReScanMergeAppend((MergeAppendState *) node);
159 : 12 : break;
160 : :
6606 161 : 8 : case T_RecursiveUnionState:
5960 162 : 8 : ExecReScanRecursiveUnion((RecursiveUnionState *) node);
6606 163 : 8 : break;
164 : :
7870 165 : 86 : case T_BitmapAndState:
5960 166 : 86 : ExecReScanBitmapAnd((BitmapAndState *) node);
7870 167 : 86 : break;
168 : :
169 : 4 : case T_BitmapOrState:
5960 170 : 4 : ExecReScanBitmapOr((BitmapOrState *) node);
7870 171 : 4 : break;
172 : :
8736 173 : 659803 : case T_SeqScanState:
5960 174 : 659803 : ExecReScanSeqScan((SeqScanState *) node);
9533 175 : 659803 : break;
176 : :
4192 simon@2ndQuadrant.co 177 : 65 : case T_SampleScanState:
178 : 65 : ExecReScanSampleScan((SampleScanState *) node);
179 : 65 : break;
180 : :
4054 rhaas@postgresql.org 181 : 212 : case T_GatherState:
182 : 212 : ExecReScanGather((GatherState *) node);
183 : 212 : break;
184 : :
3369 185 : 32 : case T_GatherMergeState:
186 : 32 : ExecReScanGatherMerge((GatherMergeState *) node);
187 : 32 : break;
188 : :
8736 tgl@sss.pgh.pa.us 189 : 345268 : case T_IndexScanState:
5960 190 : 345268 : ExecReScanIndexScan((IndexScanState *) node);
9533 191 : 345268 : break;
192 : :
5504 193 : 143109 : case T_IndexOnlyScanState:
194 : 143109 : ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
195 : 143109 : break;
196 : :
7870 197 : 3130 : case T_BitmapIndexScanState:
5960 198 : 3130 : ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
7870 199 : 3130 : break;
200 : :
201 : 2772 : case T_BitmapHeapScanState:
5960 202 : 2772 : ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
7870 203 : 2772 : break;
204 : :
8736 205 : 7677 : case T_TidScanState:
5960 206 : 7677 : ExecReScanTidScan((TidScanState *) node);
8943 207 : 7677 : break;
208 : :
2077 drowley@postgresql.o 209 : 80 : case T_TidRangeScanState:
210 : 80 : ExecReScanTidRangeScan((TidRangeScanState *) node);
211 : 80 : break;
212 : :
8736 tgl@sss.pgh.pa.us 213 : 495 : case T_SubqueryScanState:
5960 214 : 495 : ExecReScanSubqueryScan((SubqueryScanState *) node);
10492 vadim4o@yahoo.com 215 : 495 : break;
216 : :
8736 tgl@sss.pgh.pa.us 217 : 73272 : case T_FunctionScanState:
5960 218 : 73272 : ExecReScanFunctionScan((FunctionScanState *) node);
10492 vadim4o@yahoo.com 219 : 73272 : break;
220 : :
3529 alvherre@alvh.no-ip. 221 : 344 : case T_TableFuncScanState:
222 : 344 : ExecReScanTableFuncScan((TableFuncScanState *) node);
223 : 344 : break;
224 : :
7400 mail@joeconway.com 225 : 40262 : case T_ValuesScanState:
5960 tgl@sss.pgh.pa.us 226 : 40262 : ExecReScanValuesScan((ValuesScanState *) node);
7400 mail@joeconway.com 227 : 40262 : break;
228 : :
6606 tgl@sss.pgh.pa.us 229 : 5020 : case T_CteScanState:
5960 230 : 5020 : ExecReScanCteScan((CteScanState *) node);
6606 231 : 5020 : break;
232 : :
3506 kgrittn@postgresql.o 233 :UBC 0 : case T_NamedTuplestoreScanState:
234 : 0 : ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
235 : 0 : break;
236 : :
6606 tgl@sss.pgh.pa.us 237 :CBC 4440 : case T_WorkTableScanState:
5960 238 : 4440 : ExecReScanWorkTableScan((WorkTableScanState *) node);
6606 239 : 4440 : break;
240 : :
5737 241 : 448 : case T_ForeignScanState:
242 : 448 : ExecReScanForeignScan((ForeignScanState *) node);
243 : 448 : break;
244 : :
4381 rhaas@postgresql.org 245 :UBC 0 : case T_CustomScanState:
246 : 0 : ExecReScanCustomScan((CustomScanState *) node);
247 : 0 : break;
248 : :
8736 tgl@sss.pgh.pa.us 249 :CBC 48927 : case T_NestLoopState:
5960 250 : 48927 : ExecReScanNestLoop((NestLoopState *) node);
10492 vadim4o@yahoo.com 251 : 48927 : break;
252 : :
8736 tgl@sss.pgh.pa.us 253 : 596 : case T_MergeJoinState:
5960 254 : 596 : ExecReScanMergeJoin((MergeJoinState *) node);
10492 vadim4o@yahoo.com 255 : 596 : break;
256 : :
8736 tgl@sss.pgh.pa.us 257 : 3199 : case T_HashJoinState:
5960 258 : 3199 : ExecReScanHashJoin((HashJoinState *) node);
10492 vadim4o@yahoo.com 259 : 3199 : break;
260 : :
8736 tgl@sss.pgh.pa.us 261 : 137168 : case T_MaterialState:
5960 262 : 137168 : ExecReScanMaterial((MaterialState *) node);
10339 bruce@momjian.us 263 : 137168 : break;
264 : :
1940 drowley@postgresql.o 265 : 866681 : case T_MemoizeState:
266 : 866681 : ExecReScanMemoize((MemoizeState *) node);
2043 267 : 866681 : break;
268 : :
8736 tgl@sss.pgh.pa.us 269 : 28417 : case T_SortState:
5960 270 : 28417 : ExecReScanSort((SortState *) node);
10492 vadim4o@yahoo.com 271 : 28417 : break;
272 : :
2404 tomas.vondra@postgre 273 : 8 : case T_IncrementalSortState:
274 : 8 : ExecReScanIncrementalSort((IncrementalSortState *) node);
275 : 8 : break;
276 : :
8736 tgl@sss.pgh.pa.us 277 : 15 : case T_GroupState:
5960 278 : 15 : ExecReScanGroup((GroupState *) node);
10482 vadim4o@yahoo.com 279 : 15 : break;
280 : :
8736 tgl@sss.pgh.pa.us 281 : 31533 : case T_AggState:
5960 282 : 31533 : ExecReScanAgg((AggState *) node);
9527 283 : 31533 : break;
284 : :
6521 285 : 52 : case T_WindowAggState:
5960 286 : 52 : ExecReScanWindowAgg((WindowAggState *) node);
6521 287 : 52 : break;
288 : :
8736 289 : 24 : case T_UniqueState:
5960 290 : 24 : ExecReScanUnique((UniqueState *) node);
9506 291 : 24 : break;
292 : :
8736 293 : 1706 : case T_HashState:
5960 294 : 1706 : ExecReScanHash((HashState *) node);
10482 vadim4o@yahoo.com 295 : 1706 : break;
296 : :
8736 tgl@sss.pgh.pa.us 297 : 800 : case T_SetOpState:
5960 298 : 800 : ExecReScanSetOp((SetOpState *) node);
10478 vadim4o@yahoo.com 299 : 800 : break;
300 : :
6233 tgl@sss.pgh.pa.us 301 : 8 : case T_LockRowsState:
5960 302 : 8 : ExecReScanLockRows((LockRowsState *) node);
6233 303 : 8 : break;
304 : :
8736 305 : 40665 : case T_LimitState:
5960 306 : 40665 : ExecReScanLimit((LimitState *) node);
10340 bruce@momjian.us 307 : 40665 : break;
308 : :
10650 bruce@momjian.us 309 :UBC 0 : default:
8508 tgl@sss.pgh.pa.us 310 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
311 : : break;
312 : : }
313 : :
8670 tgl@sss.pgh.pa.us 314 [ + + ]:CBC 2576989 : if (node->chgParam != NULL)
315 : : {
316 : 2330589 : bms_free(node->chgParam);
317 : 2330589 : node->chgParam = NULL;
318 : : }
11076 scrappy@hub.org 319 : 2576989 : }
320 : :
321 : : /*
322 : : * ExecMarkPos
323 : : *
324 : : * Marks the current scan position.
325 : : *
326 : : * NOTE: mark/restore capability is currently needed only for plan nodes
327 : : * that are the immediate inner child of a MergeJoin node. Since MergeJoin
328 : : * requires sorted input, there is never any need to support mark/restore in
329 : : * node types that cannot produce sorted output. There are some cases in
330 : : * which a node can pass through sorted data from its child; if we don't
331 : : * implement mark/restore for such a node type, the planner compensates by
332 : : * inserting a Material node above that node.
333 : : */
334 : : void
8490 bruce@momjian.us 335 : 451719 : ExecMarkPos(PlanState *node)
336 : : {
10651 337 [ + + - + : 451719 : switch (nodeTag(node))
+ - - ]
338 : : {
8736 tgl@sss.pgh.pa.us 339 : 4384 : case T_IndexScanState:
340 : 4384 : ExecIndexMarkPos((IndexScanState *) node);
10650 bruce@momjian.us 341 : 4384 : break;
342 : :
5504 tgl@sss.pgh.pa.us 343 : 86019 : case T_IndexOnlyScanState:
344 : 86019 : ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
345 : 86019 : break;
346 : :
4381 rhaas@postgresql.org 347 :UBC 0 : case T_CustomScanState:
348 : 0 : ExecCustomMarkPos((CustomScanState *) node);
349 : 0 : break;
350 : :
8736 tgl@sss.pgh.pa.us 351 :CBC 4341 : case T_MaterialState:
352 : 4341 : ExecMaterialMarkPos((MaterialState *) node);
9636 353 : 4341 : break;
354 : :
8736 355 : 356975 : case T_SortState:
356 : 356975 : ExecSortMarkPos((SortState *) node);
10650 bruce@momjian.us 357 : 356975 : break;
358 : :
7203 tgl@sss.pgh.pa.us 359 :UBC 0 : case T_ResultState:
360 : 0 : ExecResultMarkPos((ResultState *) node);
361 : 0 : break;
362 : :
10650 bruce@momjian.us 363 : 0 : default:
364 : : /* don't make hard error unless caller asks to restore... */
8508 tgl@sss.pgh.pa.us 365 [ # # ]: 0 : elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
10650 bruce@momjian.us 366 : 0 : break;
367 : : }
11076 scrappy@hub.org 368 :CBC 451719 : }
369 : :
370 : : /*
371 : : * ExecRestrPos
372 : : *
373 : : * restores the scan position previously saved with ExecMarkPos()
374 : : *
375 : : * NOTE: the semantics of this are that the first ExecProcNode following
376 : : * the restore operation will yield the same tuple as the first one following
377 : : * the mark operation. It is unspecified what happens to the plan node's
378 : : * result TupleTableSlot. (In most cases the result slot is unchanged by
379 : : * a restore, but the node may choose to clear it or to load it with the
380 : : * restored-to tuple.) Hence the caller should discard any previously
381 : : * returned TupleTableSlot after doing a restore.
382 : : */
383 : : void
8490 bruce@momjian.us 384 : 116457 : ExecRestrPos(PlanState *node)
385 : : {
10651 386 [ + + - + : 116457 : switch (nodeTag(node))
+ - - ]
387 : : {
8736 tgl@sss.pgh.pa.us 388 : 36018 : case T_IndexScanState:
389 : 36018 : ExecIndexRestrPos((IndexScanState *) node);
9599 390 : 36018 : break;
391 : :
5504 392 : 16000 : case T_IndexOnlyScanState:
393 : 16000 : ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
394 : 16000 : break;
395 : :
4381 rhaas@postgresql.org 396 :UBC 0 : case T_CustomScanState:
397 : 0 : ExecCustomRestrPos((CustomScanState *) node);
398 : 0 : break;
399 : :
8736 tgl@sss.pgh.pa.us 400 :CBC 36132 : case T_MaterialState:
401 : 36132 : ExecMaterialRestrPos((MaterialState *) node);
9599 402 : 36132 : break;
403 : :
8736 404 : 28307 : case T_SortState:
405 : 28307 : ExecSortRestrPos((SortState *) node);
9599 406 : 28307 : break;
407 : :
7203 tgl@sss.pgh.pa.us 408 :UBC 0 : case T_ResultState:
409 : 0 : ExecResultRestrPos((ResultState *) node);
410 : 0 : break;
411 : :
10650 bruce@momjian.us 412 : 0 : default:
8508 tgl@sss.pgh.pa.us 413 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
414 : : break;
415 : : }
11076 scrappy@hub.org 416 :CBC 116457 : }
417 : :
418 : : /*
419 : : * ExecSupportsMarkRestore - does a Path support mark/restore?
420 : : *
421 : : * This is used during planning and so must accept a Path, not a Plan.
422 : : * We keep it here to be adjacent to the routines above, which also must
423 : : * know which plan types support mark/restore.
424 : : */
425 : : bool
4381 rhaas@postgresql.org 426 : 7019 : ExecSupportsMarkRestore(Path *pathnode)
427 : : {
428 : : /*
429 : : * For consistency with the routines above, we do not examine the nodeTag
430 : : * but rather the pathtype, which is the Plan node type the Path would
431 : : * produce.
432 : : */
433 [ + - - - : 7019 : switch (pathnode->pathtype)
+ + + ]
434 : : {
8741 tgl@sss.pgh.pa.us 435 : 4714 : case T_IndexScan:
436 : : case T_IndexOnlyScan:
437 : :
438 : : /*
439 : : * Not all index types support mark/restore.
440 : : */
2172 rhodiumtoad@postgres 441 : 4714 : return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
442 : :
8741 tgl@sss.pgh.pa.us 443 :UBC 0 : case T_Material:
444 : : case T_Sort:
445 : 0 : return true;
446 : :
4368 447 : 0 : case T_CustomScan:
1948 448 [ # # ]: 0 : if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
449 : 0 : return true;
450 : 0 : return false;
451 : :
7203 452 : 0 : case T_Result:
453 : :
454 : : /*
455 : : * Result supports mark/restore iff it has a child plan that does.
456 : : *
457 : : * We have to be careful here because there is more than one Path
458 : : * type that can produce a Result plan node.
459 : : */
3895 460 [ # # ]: 0 : if (IsA(pathnode, ProjectionPath))
461 : 0 : return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath);
462 [ # # ]: 0 : else if (IsA(pathnode, MinMaxAggPath))
463 : 0 : return false; /* childless Result */
2838 464 [ # # ]: 0 : else if (IsA(pathnode, GroupResultPath))
465 : 0 : return false; /* childless Result */
466 : : else
467 : : {
468 : : /* Simple RTE_RESULT base relation */
469 [ # # ]: 0 : Assert(IsA(pathnode, Path));
3895 470 : 0 : return false; /* childless Result */
471 : : }
472 : :
2782 tgl@sss.pgh.pa.us 473 :CBC 484 : case T_Append:
474 : : {
475 : 484 : AppendPath *appendPath = castNode(AppendPath, pathnode);
476 : :
477 : : /*
478 : : * If there's exactly one child, then there will be no Append
479 : : * in the final plan, so we can handle mark/restore if the
480 : : * child plan node can.
481 : : */
482 [ - + ]: 484 : if (list_length(appendPath->subpaths) == 1)
2782 tgl@sss.pgh.pa.us 483 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
484 : : /* Otherwise, Append can't handle it */
2782 tgl@sss.pgh.pa.us 485 :CBC 484 : return false;
486 : : }
487 : :
488 : 24 : case T_MergeAppend:
489 : : {
490 : 24 : MergeAppendPath *mapath = castNode(MergeAppendPath, pathnode);
491 : :
492 : : /*
493 : : * Like the Append case above, single-subpath MergeAppends
494 : : * won't be in the final plan, so just return the child's
495 : : * mark/restore ability.
496 : : */
497 [ - + ]: 24 : if (list_length(mapath->subpaths) == 1)
2782 tgl@sss.pgh.pa.us 498 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
499 : : /* Otherwise, MergeAppend can't handle it */
2782 tgl@sss.pgh.pa.us 500 :CBC 24 : return false;
501 : : }
502 : :
8741 503 : 1797 : default:
504 : 1797 : break;
505 : : }
506 : :
507 : 1797 : return false;
508 : : }
509 : :
510 : : /*
511 : : * ExecSupportsBackwardScan - does a plan type support backwards scanning?
512 : : *
513 : : * Ideally, all plan types would support backwards scan, but that seems
514 : : * unlikely to happen soon. In some cases, a plan node passes the backwards
515 : : * scan down to its children, and so supports backwards scan only if its
516 : : * children do. Therefore, this routine must be passed a complete plan tree.
517 : : */
518 : : bool
8641 519 : 3079 : ExecSupportsBackwardScan(Plan *node)
520 : : {
521 [ - + ]: 3079 : if (node == NULL)
8641 tgl@sss.pgh.pa.us 522 :UBC 0 : return false;
523 : :
524 : : /*
525 : : * Parallel-aware nodes return a subset of the tuples in each worker, and
526 : : * in general we can't expect to have enough bookkeeping state to know
527 : : * which ones we returned in this worker as opposed to some other worker.
528 : : */
4012 rhaas@postgresql.org 529 [ - + ]:CBC 3079 : if (node->parallel_aware)
4012 rhaas@postgresql.org 530 :UBC 0 : return false;
531 : :
8641 tgl@sss.pgh.pa.us 532 [ + + + - :CBC 3079 : switch (nodeTag(node))
+ + - - +
+ + + ]
533 : : {
534 : 49 : case T_Result:
535 [ - + ]: 49 : if (outerPlan(node) != NULL)
3577 andres@anarazel.de 536 :UBC 0 : return ExecSupportsBackwardScan(outerPlan(node));
537 : : else
8641 tgl@sss.pgh.pa.us 538 :CBC 49 : return false;
539 : :
540 : 32 : case T_Append:
541 : : {
542 : : ListCell *l;
543 : :
544 : : /* With async, tuples may be interleaved, so can't back up. */
2045 efujita@postgresql.o 545 [ - + ]: 32 : if (((Append *) node)->nasyncplans > 0)
2045 efujita@postgresql.o 546 :UBC 0 : return false;
547 : :
8494 bruce@momjian.us 548 [ + - + + :CBC 110 : foreach(l, ((Append *) node)->appendplans)
+ + ]
549 : : {
550 [ + + ]: 79 : if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
551 : 1 : return false;
552 : : }
553 : : /* need not check tlist because Append doesn't evaluate it */
554 : 31 : return true;
555 : : }
556 : :
4121 tgl@sss.pgh.pa.us 557 : 5 : case T_SampleScan:
558 : : /* Simplify life for tablesample methods by disallowing this */
559 : 5 : return false;
560 : :
4054 rhaas@postgresql.org 561 :UBC 0 : case T_Gather:
562 : 0 : return false;
563 : :
6593 tgl@sss.pgh.pa.us 564 :CBC 242 : case T_IndexScan:
3577 andres@anarazel.de 565 : 242 : return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
566 : :
5504 tgl@sss.pgh.pa.us 567 : 27 : case T_IndexOnlyScan:
3577 andres@anarazel.de 568 : 27 : return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
569 : :
8641 tgl@sss.pgh.pa.us 570 :UBC 0 : case T_SubqueryScan:
3577 andres@anarazel.de 571 : 0 : return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
572 : :
4381 rhaas@postgresql.org 573 : 0 : case T_CustomScan:
1948 tgl@sss.pgh.pa.us 574 [ # # ]: 0 : if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
575 : 0 : return true;
4381 rhaas@postgresql.org 576 : 0 : return false;
577 : :
3577 andres@anarazel.de 578 :CBC 2225 : case T_SeqScan:
579 : : case T_TidScan:
580 : : case T_TidRangeScan:
581 : : case T_FunctionScan:
582 : : case T_ValuesScan:
583 : : case T_CteScan:
584 : : case T_Material:
585 : : case T_Sort:
586 : : /* these don't evaluate tlist */
8641 tgl@sss.pgh.pa.us 587 : 2225 : return true;
588 : :
2404 tomas.vondra@postgre 589 : 2 : case T_IncrementalSort:
590 : :
591 : : /*
592 : : * Unlike full sort, incremental sort keeps only a single group of
593 : : * tuples in memory, so it can't scan backwards.
594 : : */
595 : 2 : return false;
596 : :
6233 tgl@sss.pgh.pa.us 597 : 83 : case T_LockRows:
598 : : case T_Limit:
8641 599 : 83 : return ExecSupportsBackwardScan(outerPlan(node));
600 : :
601 : 414 : default:
602 : 414 : return false;
603 : : }
604 : : }
605 : :
606 : : /*
607 : : * An IndexScan or IndexOnlyScan node supports backward scan only if the
608 : : * index's AM does.
609 : : */
610 : : static bool
6593 611 : 269 : IndexSupportsBackwardScan(Oid indexid)
612 : : {
613 : : bool result;
614 : : HeapTuple ht_idxrel;
615 : : Form_pg_class idxrelrec;
616 : : const IndexAmRoutine *amroutine;
617 : :
618 : : /* Fetch the pg_class tuple of the index relation */
6108 rhaas@postgresql.org 619 : 269 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
6593 tgl@sss.pgh.pa.us 620 [ - + ]: 269 : if (!HeapTupleIsValid(ht_idxrel))
6593 tgl@sss.pgh.pa.us 621 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", indexid);
6593 tgl@sss.pgh.pa.us 622 :CBC 269 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
623 : :
624 : : /* Fetch the index AM's API struct */
3736 625 : 269 : amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
626 : :
3945 627 : 269 : result = amroutine->amcanbackward;
628 : :
6593 629 : 269 : ReleaseSysCache(ht_idxrel);
630 : :
631 : 269 : return result;
632 : : }
633 : :
634 : : /*
635 : : * ExecMaterializesOutput - does a plan type materialize its output?
636 : : *
637 : : * Returns true if the plan node type is one that automatically materializes
638 : : * its output (typically by keeping it in a tuplestore). For such plans,
639 : : * a rescan without any parameter change will have zero startup cost and
640 : : * very low per-tuple cost.
641 : : */
642 : : bool
6263 643 : 527361 : ExecMaterializesOutput(NodeTag plantype)
644 : : {
645 [ + + ]: 527361 : switch (plantype)
646 : : {
647 : 16889 : case T_Material:
648 : : case T_FunctionScan:
649 : : case T_TableFuncScan:
650 : : case T_CteScan:
651 : : case T_NamedTuplestoreScan:
652 : : case T_WorkTableScan:
653 : : case T_Sort:
654 : 16889 : return true;
655 : :
656 : 510472 : default:
657 : 510472 : break;
658 : : }
659 : :
660 : 510472 : return false;
661 : : }
|