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
5801 tgl@sss.pgh.pa.us 78 :CBC 2379274 : ExecReScan(PlanState *node)
79 : : {
80 : : /* If collecting timing stats, update them */
9020 81 [ + + ]: 2379274 : if (node->instrument)
82 : 28599 : 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 : : */
8511 97 [ + + ]: 2379274 : if (node->chgParam != NULL)
98 : : {
99 : : ListCell *l;
100 : :
8039 neilc@samurai.com 101 [ + + + + : 2217582 : foreach(l, node->initPlan)
+ + ]
102 : : {
103 : 1097 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8569 tgl@sss.pgh.pa.us 104 : 1097 : PlanState *splan = sstate->planstate;
105 : :
8511 106 [ + + ]: 1097 : if (splan->plan->extParam != NULL) /* don't care about child
107 : : * local Params */
108 : 1010 : UpdateChangedParamSet(splan, node->chgParam);
109 [ + + ]: 1097 : if (splan->chgParam != NULL)
8569 110 : 850 : ExecReScanSetParamPlan(sstate, node);
111 : : }
8039 neilc@samurai.com 112 [ + + + + : 2217059 : foreach(l, node->subPlan)
+ + ]
113 : : {
114 : 574 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8569 tgl@sss.pgh.pa.us 115 : 574 : PlanState *splan = sstate->planstate;
116 : :
8511 117 [ + + ]: 574 : if (splan->plan->extParam != NULL)
118 : 570 : UpdateChangedParamSet(splan, node->chgParam);
119 : : }
120 : : /* Well. Now set chgParam for child trees. */
1423 121 [ + + ]: 2216485 : if (outerPlanState(node) != NULL)
122 : 733164 : UpdateChangedParamSet(outerPlanState(node), node->chgParam);
123 [ + + ]: 2216485 : if (innerPlanState(node) != NULL)
124 : 11920 : UpdateChangedParamSet(innerPlanState(node), node->chgParam);
125 : : }
126 : :
127 : : /* Call expression callbacks */
8199 128 [ + + ]: 2379274 : if (node->ps_ExprContext)
129 : 2244925 : ReScanExprContext(node->ps_ExprContext);
130 : :
131 : : /* And do node-type-specific processing */
10492 bruce@momjian.us 132 : 2379274 : switch (nodeTag(node))
[ + + - +
+ + + + +
+ + + + +
+ + + + +
+ + + + -
+ + - + +
+ + + + +
+ + + + +
+ + + - ]
133 : : {
8577 tgl@sss.pgh.pa.us 134 : 29177 : case T_ResultState:
5801 135 : 29177 : ExecReScanResult((ResultState *) node);
10333 vadim4o@yahoo.com 136 : 29177 : break;
137 : :
3419 andres@anarazel.de 138 : 10842 : case T_ProjectSetState:
139 : 10842 : ExecReScanProjectSet((ProjectSetState *) node);
140 : 10842 : break;
141 : :
6076 tgl@sss.pgh.pa.us 142 :UBC 0 : case T_ModifyTableState:
5801 143 : 0 : ExecReScanModifyTable((ModifyTableState *) node);
6076 144 : 0 : break;
145 : :
8577 tgl@sss.pgh.pa.us 146 :CBC 13379 : case T_AppendState:
5801 147 : 13379 : ExecReScanAppend((AppendState *) node);
10333 vadim4o@yahoo.com 148 : 13379 : break;
149 : :
5707 tgl@sss.pgh.pa.us 150 : 12 : case T_MergeAppendState:
151 : 12 : ExecReScanMergeAppend((MergeAppendState *) node);
152 : 12 : break;
153 : :
6447 154 : 8 : case T_RecursiveUnionState:
5801 155 : 8 : ExecReScanRecursiveUnion((RecursiveUnionState *) node);
6447 156 : 8 : break;
157 : :
7711 158 : 82 : case T_BitmapAndState:
5801 159 : 82 : ExecReScanBitmapAnd((BitmapAndState *) node);
7711 160 : 82 : break;
161 : :
162 : 25 : case T_BitmapOrState:
5801 163 : 25 : ExecReScanBitmapOr((BitmapOrState *) node);
7711 164 : 25 : break;
165 : :
8577 166 : 882813 : case T_SeqScanState:
5801 167 : 882813 : ExecReScanSeqScan((SeqScanState *) node);
9374 168 : 882813 : break;
169 : :
4033 simon@2ndQuadrant.co 170 : 37 : case T_SampleScanState:
171 : 37 : ExecReScanSampleScan((SampleScanState *) node);
172 : 37 : break;
173 : :
3895 rhaas@postgresql.org 174 : 200 : case T_GatherState:
175 : 200 : ExecReScanGather((GatherState *) node);
176 : 200 : break;
177 : :
3210 178 : 32 : case T_GatherMergeState:
179 : 32 : ExecReScanGatherMerge((GatherMergeState *) node);
180 : 32 : break;
181 : :
8577 tgl@sss.pgh.pa.us 182 : 359928 : case T_IndexScanState:
5801 183 : 359928 : ExecReScanIndexScan((IndexScanState *) node);
9374 184 : 359928 : break;
185 : :
5345 186 : 148300 : case T_IndexOnlyScanState:
187 : 148300 : ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
188 : 148300 : break;
189 : :
7711 190 : 3276 : case T_BitmapIndexScanState:
5801 191 : 3276 : ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
7711 192 : 3276 : break;
193 : :
194 : 2838 : case T_BitmapHeapScanState:
5801 195 : 2838 : ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
7711 196 : 2838 : break;
197 : :
8577 198 : 12 : case T_TidScanState:
5801 199 : 12 : ExecReScanTidScan((TidScanState *) node);
8784 200 : 12 : break;
201 : :
1918 drowley@postgresql.o 202 : 64 : case T_TidRangeScanState:
203 : 64 : ExecReScanTidRangeScan((TidRangeScanState *) node);
204 : 64 : break;
205 : :
8577 tgl@sss.pgh.pa.us 206 : 464 : case T_SubqueryScanState:
5801 207 : 464 : ExecReScanSubqueryScan((SubqueryScanState *) node);
10333 vadim4o@yahoo.com 208 : 464 : break;
209 : :
8577 tgl@sss.pgh.pa.us 210 : 72756 : case T_FunctionScanState:
5801 211 : 72756 : ExecReScanFunctionScan((FunctionScanState *) node);
10333 vadim4o@yahoo.com 212 : 72756 : break;
213 : :
3370 alvherre@alvh.no-ip. 214 : 296 : case T_TableFuncScanState:
215 : 296 : ExecReScanTableFuncScan((TableFuncScanState *) node);
216 : 296 : break;
217 : :
7241 mail@joeconway.com 218 : 40267 : case T_ValuesScanState:
5801 tgl@sss.pgh.pa.us 219 : 40267 : ExecReScanValuesScan((ValuesScanState *) node);
7241 mail@joeconway.com 220 : 40267 : break;
221 : :
6447 tgl@sss.pgh.pa.us 222 : 3401 : case T_CteScanState:
5801 223 : 3401 : ExecReScanCteScan((CteScanState *) node);
6447 224 : 3401 : break;
225 : :
3347 kgrittn@postgresql.o 226 :UBC 0 : case T_NamedTuplestoreScanState:
227 : 0 : ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
228 : 0 : break;
229 : :
6447 tgl@sss.pgh.pa.us 230 :CBC 4280 : case T_WorkTableScanState:
5801 231 : 4280 : ExecReScanWorkTableScan((WorkTableScanState *) node);
6447 232 : 4280 : break;
233 : :
5578 234 : 410 : case T_ForeignScanState:
235 : 410 : ExecReScanForeignScan((ForeignScanState *) node);
236 : 410 : break;
237 : :
4222 rhaas@postgresql.org 238 :UBC 0 : case T_CustomScanState:
239 : 0 : ExecReScanCustomScan((CustomScanState *) node);
240 : 0 : break;
241 : :
8577 tgl@sss.pgh.pa.us 242 :CBC 9149 : case T_NestLoopState:
5801 243 : 9149 : ExecReScanNestLoop((NestLoopState *) node);
10333 vadim4o@yahoo.com 244 : 9149 : break;
245 : :
8577 tgl@sss.pgh.pa.us 246 : 345 : case T_MergeJoinState:
5801 247 : 345 : ExecReScanMergeJoin((MergeJoinState *) node);
10333 vadim4o@yahoo.com 248 : 345 : break;
249 : :
8577 tgl@sss.pgh.pa.us 250 : 2242 : case T_HashJoinState:
5801 251 : 2242 : ExecReScanHashJoin((HashJoinState *) node);
10333 vadim4o@yahoo.com 252 : 2242 : break;
253 : :
8577 tgl@sss.pgh.pa.us 254 : 88902 : case T_MaterialState:
5801 255 : 88902 : ExecReScanMaterial((MaterialState *) node);
10180 bruce@momjian.us 256 : 88902 : break;
257 : :
1781 drowley@postgresql.o 258 : 602937 : case T_MemoizeState:
259 : 602937 : ExecReScanMemoize((MemoizeState *) node);
1884 260 : 602937 : break;
261 : :
8577 tgl@sss.pgh.pa.us 262 : 28649 : case T_SortState:
5801 263 : 28649 : ExecReScanSort((SortState *) node);
10333 vadim4o@yahoo.com 264 : 28649 : break;
265 : :
2245 tomas.vondra@postgre 266 : 8 : case T_IncrementalSortState:
267 : 8 : ExecReScanIncrementalSort((IncrementalSortState *) node);
268 : 8 : break;
269 : :
8577 tgl@sss.pgh.pa.us 270 : 15 : case T_GroupState:
5801 271 : 15 : ExecReScanGroup((GroupState *) node);
10323 vadim4o@yahoo.com 272 : 15 : break;
273 : :
8577 tgl@sss.pgh.pa.us 274 : 31511 : case T_AggState:
5801 275 : 31511 : ExecReScanAgg((AggState *) node);
9368 276 : 31511 : break;
277 : :
6362 278 : 52 : case T_WindowAggState:
5801 279 : 52 : ExecReScanWindowAgg((WindowAggState *) node);
6362 280 : 52 : break;
281 : :
8577 tgl@sss.pgh.pa.us 282 :GBC 24 : case T_UniqueState:
5801 283 : 24 : ExecReScanUnique((UniqueState *) node);
9347 284 : 24 : break;
285 : :
8577 tgl@sss.pgh.pa.us 286 :CBC 1056 : case T_HashState:
5801 287 : 1056 : ExecReScanHash((HashState *) node);
10323 vadim4o@yahoo.com 288 : 1056 : break;
289 : :
8577 tgl@sss.pgh.pa.us 290 : 800 : case T_SetOpState:
5801 291 : 800 : ExecReScanSetOp((SetOpState *) node);
10319 vadim4o@yahoo.com 292 : 800 : break;
293 : :
6074 tgl@sss.pgh.pa.us 294 : 8 : case T_LockRowsState:
5801 295 : 8 : ExecReScanLockRows((LockRowsState *) node);
6074 296 : 8 : break;
297 : :
8577 298 : 40677 : case T_LimitState:
5801 299 : 40677 : ExecReScanLimit((LimitState *) node);
10181 bruce@momjian.us 300 : 40677 : break;
301 : :
10491 bruce@momjian.us 302 :UBC 0 : default:
8349 tgl@sss.pgh.pa.us 303 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
304 : : break;
305 : : }
306 : :
8511 tgl@sss.pgh.pa.us 307 [ + + ]:CBC 2379274 : if (node->chgParam != NULL)
308 : : {
309 : 2216485 : bms_free(node->chgParam);
310 : 2216485 : node->chgParam = NULL;
311 : : }
10917 scrappy@hub.org 312 : 2379274 : }
313 : :
314 : : /*
315 : : * ExecMarkPos
316 : : *
317 : : * Marks the current scan position.
318 : : *
319 : : * NOTE: mark/restore capability is currently needed only for plan nodes
320 : : * that are the immediate inner child of a MergeJoin node. Since MergeJoin
321 : : * requires sorted input, there is never any need to support mark/restore in
322 : : * node types that cannot produce sorted output. There are some cases in
323 : : * which a node can pass through sorted data from its child; if we don't
324 : : * implement mark/restore for such a node type, the planner compensates by
325 : : * inserting a Material node above that node.
326 : : */
327 : : void
8331 bruce@momjian.us 328 : 483448 : ExecMarkPos(PlanState *node)
329 : : {
10492 330 [ + + - + : 483448 : switch (nodeTag(node))
+ - - ]
331 : : {
8577 tgl@sss.pgh.pa.us 332 : 4061 : case T_IndexScanState:
333 : 4061 : ExecIndexMarkPos((IndexScanState *) node);
10491 bruce@momjian.us 334 : 4061 : break;
335 : :
5345 tgl@sss.pgh.pa.us 336 : 82019 : case T_IndexOnlyScanState:
337 : 82019 : ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
338 : 82019 : break;
339 : :
4222 rhaas@postgresql.org 340 :UBC 0 : case T_CustomScanState:
341 : 0 : ExecCustomMarkPos((CustomScanState *) node);
342 : 0 : break;
343 : :
8577 tgl@sss.pgh.pa.us 344 :CBC 4332 : case T_MaterialState:
345 : 4332 : ExecMaterialMarkPos((MaterialState *) node);
9477 346 : 4332 : break;
347 : :
8577 348 : 393036 : case T_SortState:
349 : 393036 : ExecSortMarkPos((SortState *) node);
10491 bruce@momjian.us 350 : 393036 : break;
351 : :
7044 tgl@sss.pgh.pa.us 352 :UBC 0 : case T_ResultState:
353 : 0 : ExecResultMarkPos((ResultState *) node);
354 : 0 : break;
355 : :
10491 bruce@momjian.us 356 : 0 : default:
357 : : /* don't make hard error unless caller asks to restore... */
8349 tgl@sss.pgh.pa.us 358 [ # # ]: 0 : elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
10491 bruce@momjian.us 359 : 0 : break;
360 : : }
10917 scrappy@hub.org 361 :CBC 483448 : }
362 : :
363 : : /*
364 : : * ExecRestrPos
365 : : *
366 : : * restores the scan position previously saved with ExecMarkPos()
367 : : *
368 : : * NOTE: the semantics of this are that the first ExecProcNode following
369 : : * the restore operation will yield the same tuple as the first one following
370 : : * the mark operation. It is unspecified what happens to the plan node's
371 : : * result TupleTableSlot. (In most cases the result slot is unchanged by
372 : : * a restore, but the node may choose to clear it or to load it with the
373 : : * restored-to tuple.) Hence the caller should discard any previously
374 : : * returned TupleTableSlot after doing a restore.
375 : : */
376 : : void
8331 bruce@momjian.us 377 : 96728 : ExecRestrPos(PlanState *node)
378 : : {
10492 379 [ + - - + : 96728 : switch (nodeTag(node))
+ - - ]
380 : : {
8577 tgl@sss.pgh.pa.us 381 : 36024 : case T_IndexScanState:
382 : 36024 : ExecIndexRestrPos((IndexScanState *) node);
9440 383 : 36024 : break;
384 : :
5345 tgl@sss.pgh.pa.us 385 :UBC 0 : case T_IndexOnlyScanState:
386 : 0 : ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
387 : 0 : break;
388 : :
4222 rhaas@postgresql.org 389 : 0 : case T_CustomScanState:
390 : 0 : ExecCustomRestrPos((CustomScanState *) node);
391 : 0 : break;
392 : :
8577 tgl@sss.pgh.pa.us 393 :CBC 36020 : case T_MaterialState:
394 : 36020 : ExecMaterialRestrPos((MaterialState *) node);
9440 395 : 36020 : break;
396 : :
8577 397 : 24684 : case T_SortState:
398 : 24684 : ExecSortRestrPos((SortState *) node);
9440 399 : 24684 : break;
400 : :
7044 tgl@sss.pgh.pa.us 401 :UBC 0 : case T_ResultState:
402 : 0 : ExecResultRestrPos((ResultState *) node);
403 : 0 : break;
404 : :
10491 bruce@momjian.us 405 : 0 : default:
8349 tgl@sss.pgh.pa.us 406 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
407 : : break;
408 : : }
10917 scrappy@hub.org 409 :CBC 96728 : }
410 : :
411 : : /*
412 : : * ExecSupportsMarkRestore - does a Path support mark/restore?
413 : : *
414 : : * This is used during planning and so must accept a Path, not a Plan.
415 : : * We keep it here to be adjacent to the routines above, which also must
416 : : * know which plan types support mark/restore.
417 : : */
418 : : bool
4222 rhaas@postgresql.org 419 : 6661 : ExecSupportsMarkRestore(Path *pathnode)
420 : : {
421 : : /*
422 : : * For consistency with the routines above, we do not examine the nodeTag
423 : : * but rather the pathtype, which is the Plan node type the Path would
424 : : * produce.
425 : : */
426 [ + - - - : 6661 : switch (pathnode->pathtype)
+ + + ]
427 : : {
8582 tgl@sss.pgh.pa.us 428 : 5415 : case T_IndexScan:
429 : : case T_IndexOnlyScan:
430 : :
431 : : /*
432 : : * Not all index types support mark/restore.
433 : : */
2013 rhodiumtoad@postgres 434 : 5415 : return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
435 : :
8582 tgl@sss.pgh.pa.us 436 :UBC 0 : case T_Material:
437 : : case T_Sort:
438 : 0 : return true;
439 : :
4209 440 : 0 : case T_CustomScan:
1789 441 [ # # ]: 0 : if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
442 : 0 : return true;
443 : 0 : return false;
444 : :
7044 445 : 0 : case T_Result:
446 : :
447 : : /*
448 : : * Result supports mark/restore iff it has a child plan that does.
449 : : *
450 : : * We have to be careful here because there is more than one Path
451 : : * type that can produce a Result plan node.
452 : : */
3736 453 [ # # ]: 0 : if (IsA(pathnode, ProjectionPath))
454 : 0 : return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath);
455 [ # # ]: 0 : else if (IsA(pathnode, MinMaxAggPath))
456 : 0 : return false; /* childless Result */
2679 457 [ # # ]: 0 : else if (IsA(pathnode, GroupResultPath))
458 : 0 : return false; /* childless Result */
459 : : else
460 : : {
461 : : /* Simple RTE_RESULT base relation */
462 [ # # ]: 0 : Assert(IsA(pathnode, Path));
3736 463 : 0 : return false; /* childless Result */
464 : : }
465 : :
2623 tgl@sss.pgh.pa.us 466 :CBC 60 : case T_Append:
467 : : {
468 : 60 : AppendPath *appendPath = castNode(AppendPath, pathnode);
469 : :
470 : : /*
471 : : * If there's exactly one child, then there will be no Append
472 : : * in the final plan, so we can handle mark/restore if the
473 : : * child plan node can.
474 : : */
475 [ - + ]: 60 : if (list_length(appendPath->subpaths) == 1)
2623 tgl@sss.pgh.pa.us 476 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
477 : : /* Otherwise, Append can't handle it */
2623 tgl@sss.pgh.pa.us 478 :CBC 60 : return false;
479 : : }
480 : :
481 : 24 : case T_MergeAppend:
482 : : {
483 : 24 : MergeAppendPath *mapath = castNode(MergeAppendPath, pathnode);
484 : :
485 : : /*
486 : : * Like the Append case above, single-subpath MergeAppends
487 : : * won't be in the final plan, so just return the child's
488 : : * mark/restore ability.
489 : : */
490 [ - + ]: 24 : if (list_length(mapath->subpaths) == 1)
2623 tgl@sss.pgh.pa.us 491 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
492 : : /* Otherwise, MergeAppend can't handle it */
2623 tgl@sss.pgh.pa.us 493 :CBC 24 : return false;
494 : : }
495 : :
8582 496 : 1162 : default:
497 : 1162 : break;
498 : : }
499 : :
500 : 1162 : return false;
501 : : }
502 : :
503 : : /*
504 : : * ExecSupportsBackwardScan - does a plan type support backwards scanning?
505 : : *
506 : : * Ideally, all plan types would support backwards scan, but that seems
507 : : * unlikely to happen soon. In some cases, a plan node passes the backwards
508 : : * scan down to its children, and so supports backwards scan only if its
509 : : * children do. Therefore, this routine must be passed a complete plan tree.
510 : : */
511 : : bool
8482 512 : 3031 : ExecSupportsBackwardScan(Plan *node)
513 : : {
514 [ - + ]: 3031 : if (node == NULL)
8482 tgl@sss.pgh.pa.us 515 :UBC 0 : return false;
516 : :
517 : : /*
518 : : * Parallel-aware nodes return a subset of the tuples in each worker, and
519 : : * in general we can't expect to have enough bookkeeping state to know
520 : : * which ones we returned in this worker as opposed to some other worker.
521 : : */
3853 rhaas@postgresql.org 522 [ - + ]:CBC 3031 : if (node->parallel_aware)
3853 rhaas@postgresql.org 523 :UBC 0 : return false;
524 : :
8482 tgl@sss.pgh.pa.us 525 :CBC 3031 : switch (nodeTag(node))
[ + + + -
+ + - - +
+ + + ]
526 : : {
527 : 47 : case T_Result:
528 [ - + ]: 47 : if (outerPlan(node) != NULL)
3418 andres@anarazel.de 529 :UBC 0 : return ExecSupportsBackwardScan(outerPlan(node));
530 : : else
8482 tgl@sss.pgh.pa.us 531 :CBC 47 : return false;
532 : :
533 : 31 : case T_Append:
534 : : {
535 : : ListCell *l;
536 : :
537 : : /* With async, tuples may be interleaved, so can't back up. */
1886 efujita@postgresql.o 538 [ - + ]: 31 : if (((Append *) node)->nasyncplans > 0)
1886 efujita@postgresql.o 539 :UBC 0 : return false;
540 : :
8335 bruce@momjian.us 541 [ + - + + :CBC 107 : foreach(l, ((Append *) node)->appendplans)
+ + ]
542 : : {
543 [ + + ]: 77 : if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
544 : 1 : return false;
545 : : }
546 : : /* need not check tlist because Append doesn't evaluate it */
547 : 30 : return true;
548 : : }
549 : :
3962 tgl@sss.pgh.pa.us 550 : 5 : case T_SampleScan:
551 : : /* Simplify life for tablesample methods by disallowing this */
552 : 5 : return false;
553 : :
3895 rhaas@postgresql.org 554 :UBC 0 : case T_Gather:
555 : 0 : return false;
556 : :
6434 tgl@sss.pgh.pa.us 557 :CBC 241 : case T_IndexScan:
3418 andres@anarazel.de 558 : 241 : return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
559 : :
5345 tgl@sss.pgh.pa.us 560 : 27 : case T_IndexOnlyScan:
3418 andres@anarazel.de 561 : 27 : return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
562 : :
8482 tgl@sss.pgh.pa.us 563 :UBC 0 : case T_SubqueryScan:
3418 andres@anarazel.de 564 : 0 : return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
565 : :
4222 rhaas@postgresql.org 566 : 0 : case T_CustomScan:
1789 tgl@sss.pgh.pa.us 567 [ # # ]: 0 : if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
568 : 0 : return true;
4222 rhaas@postgresql.org 569 : 0 : return false;
570 : :
3418 andres@anarazel.de 571 :CBC 2221 : case T_SeqScan:
572 : : case T_TidScan:
573 : : case T_TidRangeScan:
574 : : case T_FunctionScan:
575 : : case T_ValuesScan:
576 : : case T_CteScan:
577 : : case T_Material:
578 : : case T_Sort:
579 : : /* these don't evaluate tlist */
8482 tgl@sss.pgh.pa.us 580 : 2221 : return true;
581 : :
2245 tomas.vondra@postgre 582 : 2 : case T_IncrementalSort:
583 : :
584 : : /*
585 : : * Unlike full sort, incremental sort keeps only a single group of
586 : : * tuples in memory, so it can't scan backwards.
587 : : */
588 : 2 : return false;
589 : :
6074 tgl@sss.pgh.pa.us 590 : 82 : case T_LockRows:
591 : : case T_Limit:
8482 592 : 82 : return ExecSupportsBackwardScan(outerPlan(node));
593 : :
594 : 375 : default:
595 : 375 : return false;
596 : : }
597 : : }
598 : :
599 : : /*
600 : : * An IndexScan or IndexOnlyScan node supports backward scan only if the
601 : : * index's AM does.
602 : : */
603 : : static bool
6434 604 : 268 : IndexSupportsBackwardScan(Oid indexid)
605 : : {
606 : : bool result;
607 : : HeapTuple ht_idxrel;
608 : : Form_pg_class idxrelrec;
609 : : const IndexAmRoutine *amroutine;
610 : :
611 : : /* Fetch the pg_class tuple of the index relation */
5949 rhaas@postgresql.org 612 : 268 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
6434 tgl@sss.pgh.pa.us 613 [ - + ]: 268 : if (!HeapTupleIsValid(ht_idxrel))
6434 tgl@sss.pgh.pa.us 614 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", indexid);
6434 tgl@sss.pgh.pa.us 615 :CBC 268 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
616 : :
617 : : /* Fetch the index AM's API struct */
3577 618 : 268 : amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
619 : :
3786 620 : 268 : result = amroutine->amcanbackward;
621 : :
6434 622 : 268 : ReleaseSysCache(ht_idxrel);
623 : :
624 : 268 : return result;
625 : : }
626 : :
627 : : /*
628 : : * ExecMaterializesOutput - does a plan type materialize its output?
629 : : *
630 : : * Returns true if the plan node type is one that automatically materializes
631 : : * its output (typically by keeping it in a tuplestore). For such plans,
632 : : * a rescan without any parameter change will have zero startup cost and
633 : : * very low per-tuple cost.
634 : : */
635 : : bool
6104 636 : 532101 : ExecMaterializesOutput(NodeTag plantype)
637 : : {
638 [ + + ]: 532101 : switch (plantype)
639 : : {
640 : 18225 : case T_Material:
641 : : case T_FunctionScan:
642 : : case T_TableFuncScan:
643 : : case T_CteScan:
644 : : case T_NamedTuplestoreScan:
645 : : case T_WorkTableScan:
646 : : case T_Sort:
647 : 18225 : return true;
648 : :
649 : 513876 : default:
650 : 513876 : break;
651 : : }
652 : :
653 : 513876 : return false;
654 : : }
|