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-2025, 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/nodeAgg.h"
20 : : #include "executor/nodeAppend.h"
21 : : #include "executor/nodeBitmapAnd.h"
22 : : #include "executor/nodeBitmapHeapscan.h"
23 : : #include "executor/nodeBitmapIndexscan.h"
24 : : #include "executor/nodeBitmapOr.h"
25 : : #include "executor/nodeCtescan.h"
26 : : #include "executor/nodeCustom.h"
27 : : #include "executor/nodeForeignscan.h"
28 : : #include "executor/nodeFunctionscan.h"
29 : : #include "executor/nodeGather.h"
30 : : #include "executor/nodeGatherMerge.h"
31 : : #include "executor/nodeGroup.h"
32 : : #include "executor/nodeHash.h"
33 : : #include "executor/nodeHashjoin.h"
34 : : #include "executor/nodeIncrementalSort.h"
35 : : #include "executor/nodeIndexonlyscan.h"
36 : : #include "executor/nodeIndexscan.h"
37 : : #include "executor/nodeLimit.h"
38 : : #include "executor/nodeLockRows.h"
39 : : #include "executor/nodeMaterial.h"
40 : : #include "executor/nodeMemoize.h"
41 : : #include "executor/nodeMergeAppend.h"
42 : : #include "executor/nodeMergejoin.h"
43 : : #include "executor/nodeModifyTable.h"
44 : : #include "executor/nodeNamedtuplestorescan.h"
45 : : #include "executor/nodeNestloop.h"
46 : : #include "executor/nodeProjectSet.h"
47 : : #include "executor/nodeRecursiveunion.h"
48 : : #include "executor/nodeResult.h"
49 : : #include "executor/nodeSamplescan.h"
50 : : #include "executor/nodeSeqscan.h"
51 : : #include "executor/nodeSetOp.h"
52 : : #include "executor/nodeSort.h"
53 : : #include "executor/nodeSubplan.h"
54 : : #include "executor/nodeSubqueryscan.h"
55 : : #include "executor/nodeTableFuncscan.h"
56 : : #include "executor/nodeTidrangescan.h"
57 : : #include "executor/nodeTidscan.h"
58 : : #include "executor/nodeUnique.h"
59 : : #include "executor/nodeValuesscan.h"
60 : : #include "executor/nodeWindowAgg.h"
61 : : #include "executor/nodeWorktablescan.h"
62 : : #include "nodes/extensible.h"
63 : : #include "nodes/pathnodes.h"
64 : : #include "utils/syscache.h"
65 : :
66 : : static bool IndexSupportsBackwardScan(Oid indexid);
67 : :
68 : :
69 : : /*
70 : : * ExecReScan
71 : : * Reset a plan node so that its output can be re-scanned.
72 : : *
73 : : * Note that if the plan node has parameters that have changed value,
74 : : * the output might be different from last time.
75 : : */
76 : : void
5535 tgl@sss.pgh.pa.us 77 :CBC 1629330 : ExecReScan(PlanState *node)
78 : : {
79 : : /* If collecting timing stats, update them */
8754 80 [ + + ]: 1629330 : if (node->instrument)
81 : 21452 : InstrEndLoop(node->instrument);
82 : :
83 : : /*
84 : : * If we have changed parameters, propagate that info.
85 : : *
86 : : * Note: ExecReScanSetParamPlan() can add bits to node->chgParam,
87 : : * corresponding to the output param(s) that the InitPlan will update.
88 : : * Since we make only one pass over the list, that means that an InitPlan
89 : : * can depend on the output param(s) of a sibling InitPlan only if that
90 : : * sibling appears earlier in the list. This is workable for now given
91 : : * the limited ways in which one InitPlan could depend on another, but
92 : : * eventually we might need to work harder (or else make the planner
93 : : * enlarge the extParam/allParam sets to include the params of depended-on
94 : : * InitPlans).
95 : : */
8245 96 [ + + ]: 1629330 : if (node->chgParam != NULL)
97 : : {
98 : : ListCell *l;
99 : :
7773 neilc@samurai.com 100 [ + + + + : 1497954 : foreach(l, node->initPlan)
+ + ]
101 : : {
102 : 880 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8303 tgl@sss.pgh.pa.us 103 : 880 : PlanState *splan = sstate->planstate;
104 : :
8245 105 [ + + ]: 880 : if (splan->plan->extParam != NULL) /* don't care about child
106 : : * local Params */
107 : 801 : UpdateChangedParamSet(splan, node->chgParam);
108 [ + + ]: 880 : if (splan->chgParam != NULL)
8303 109 : 681 : ExecReScanSetParamPlan(sstate, node);
110 : : }
7773 neilc@samurai.com 111 [ + + + + : 1497504 : foreach(l, node->subPlan)
+ + ]
112 : : {
113 : 430 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8303 tgl@sss.pgh.pa.us 114 : 430 : PlanState *splan = sstate->planstate;
115 : :
8245 116 [ + + ]: 430 : if (splan->plan->extParam != NULL)
117 : 427 : UpdateChangedParamSet(splan, node->chgParam);
118 : : }
119 : : /* Well. Now set chgParam for child trees. */
1157 120 [ + + ]: 1497074 : if (outerPlanState(node) != NULL)
121 : 461765 : UpdateChangedParamSet(outerPlanState(node), node->chgParam);
122 [ + + ]: 1497074 : if (innerPlanState(node) != NULL)
123 : 8886 : UpdateChangedParamSet(innerPlanState(node), node->chgParam);
124 : : }
125 : :
126 : : /* Call expression callbacks */
7933 127 [ + + ]: 1629330 : if (node->ps_ExprContext)
128 : 1511499 : ReScanExprContext(node->ps_ExprContext);
129 : :
130 : : /* And do node-type-specific processing */
10226 bruce@momjian.us 131 [ + + - + : 1629330 : switch (nodeTag(node))
+ + + + +
+ + + + +
+ + + + +
+ + + + -
+ + - + +
+ + + + +
+ + + + +
+ + + - ]
132 : : {
8311 tgl@sss.pgh.pa.us 133 : 20890 : case T_ResultState:
5535 134 : 20890 : ExecReScanResult((ResultState *) node);
10067 vadim4o@yahoo.com 135 : 20890 : break;
136 : :
3153 andres@anarazel.de 137 : 7676 : case T_ProjectSetState:
138 : 7676 : ExecReScanProjectSet((ProjectSetState *) node);
139 : 7676 : break;
140 : :
5810 tgl@sss.pgh.pa.us 141 :UBC 0 : case T_ModifyTableState:
5535 142 : 0 : ExecReScanModifyTable((ModifyTableState *) node);
5810 143 : 0 : break;
144 : :
8311 tgl@sss.pgh.pa.us 145 :CBC 9780 : case T_AppendState:
5535 146 : 9780 : ExecReScanAppend((AppendState *) node);
10067 vadim4o@yahoo.com 147 : 9780 : break;
148 : :
5441 tgl@sss.pgh.pa.us 149 : 9 : case T_MergeAppendState:
150 : 9 : ExecReScanMergeAppend((MergeAppendState *) node);
151 : 9 : break;
152 : :
6181 153 : 6 : case T_RecursiveUnionState:
5535 154 : 6 : ExecReScanRecursiveUnion((RecursiveUnionState *) node);
6181 155 : 6 : break;
156 : :
7445 157 : 73 : case T_BitmapAndState:
5535 158 : 73 : ExecReScanBitmapAnd((BitmapAndState *) node);
7445 159 : 73 : break;
160 : :
161 : 20 : case T_BitmapOrState:
5535 162 : 20 : ExecReScanBitmapOr((BitmapOrState *) node);
7445 163 : 20 : break;
164 : :
8311 165 : 621851 : case T_SeqScanState:
5535 166 : 621851 : ExecReScanSeqScan((SeqScanState *) node);
9108 167 : 621851 : break;
168 : :
3767 simon@2ndQuadrant.co 169 : 29 : case T_SampleScanState:
170 : 29 : ExecReScanSampleScan((SampleScanState *) node);
171 : 29 : break;
172 : :
3629 rhaas@postgresql.org 173 : 150 : case T_GatherState:
174 : 150 : ExecReScanGather((GatherState *) node);
175 : 150 : break;
176 : :
2944 177 : 24 : case T_GatherMergeState:
178 : 24 : ExecReScanGatherMerge((GatherMergeState *) node);
179 : 24 : break;
180 : :
8311 tgl@sss.pgh.pa.us 181 : 230142 : case T_IndexScanState:
5535 182 : 230142 : ExecReScanIndexScan((IndexScanState *) node);
9108 183 : 230142 : break;
184 : :
5079 185 : 110722 : case T_IndexOnlyScanState:
186 : 110722 : ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
187 : 110722 : break;
188 : :
7445 189 : 2935 : case T_BitmapIndexScanState:
5535 190 : 2935 : ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
7445 191 : 2935 : break;
192 : :
193 : 2622 : case T_BitmapHeapScanState:
5535 194 : 2622 : ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
7445 195 : 2622 : break;
196 : :
8311 197 : 9 : case T_TidScanState:
5535 198 : 9 : ExecReScanTidScan((TidScanState *) node);
8518 199 : 9 : break;
200 : :
1652 drowley@postgresql.o 201 : 33 : case T_TidRangeScanState:
202 : 33 : ExecReScanTidRangeScan((TidRangeScanState *) node);
203 : 33 : break;
204 : :
8311 tgl@sss.pgh.pa.us 205 : 229 : case T_SubqueryScanState:
5535 206 : 229 : ExecReScanSubqueryScan((SubqueryScanState *) node);
10067 vadim4o@yahoo.com 207 : 229 : break;
208 : :
8311 tgl@sss.pgh.pa.us 209 : 57341 : case T_FunctionScanState:
5535 210 : 57341 : ExecReScanFunctionScan((FunctionScanState *) node);
10067 vadim4o@yahoo.com 211 : 57341 : break;
212 : :
3104 alvherre@alvh.no-ip. 213 : 222 : case T_TableFuncScanState:
214 : 222 : ExecReScanTableFuncScan((TableFuncScanState *) node);
215 : 222 : break;
216 : :
6975 mail@joeconway.com 217 : 30189 : case T_ValuesScanState:
5535 tgl@sss.pgh.pa.us 218 : 30189 : ExecReScanValuesScan((ValuesScanState *) node);
6975 mail@joeconway.com 219 : 30189 : break;
220 : :
6181 tgl@sss.pgh.pa.us 221 : 3096 : case T_CteScanState:
5535 222 : 3096 : ExecReScanCteScan((CteScanState *) node);
6181 223 : 3096 : break;
224 : :
3081 kgrittn@postgresql.o 225 :UBC 0 : case T_NamedTuplestoreScanState:
226 : 0 : ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
227 : 0 : break;
228 : :
6181 tgl@sss.pgh.pa.us 229 :CBC 3292 : case T_WorkTableScanState:
5535 230 : 3292 : ExecReScanWorkTableScan((WorkTableScanState *) node);
6181 231 : 3292 : break;
232 : :
5312 233 : 404 : case T_ForeignScanState:
234 : 404 : ExecReScanForeignScan((ForeignScanState *) node);
235 : 404 : break;
236 : :
3956 rhaas@postgresql.org 237 :UBC 0 : case T_CustomScanState:
238 : 0 : ExecReScanCustomScan((CustomScanState *) node);
239 : 0 : break;
240 : :
8311 tgl@sss.pgh.pa.us 241 :CBC 6749 : case T_NestLoopState:
5535 242 : 6749 : ExecReScanNestLoop((NestLoopState *) node);
10067 vadim4o@yahoo.com 243 : 6749 : break;
244 : :
8311 tgl@sss.pgh.pa.us 245 : 371 : case T_MergeJoinState:
5535 246 : 371 : ExecReScanMergeJoin((MergeJoinState *) node);
10067 vadim4o@yahoo.com 247 : 371 : break;
248 : :
8311 tgl@sss.pgh.pa.us 249 : 1922 : case T_HashJoinState:
5535 250 : 1922 : ExecReScanHashJoin((HashJoinState *) node);
10067 vadim4o@yahoo.com 251 : 1922 : break;
252 : :
8311 tgl@sss.pgh.pa.us 253 : 77880 : case T_MaterialState:
5535 254 : 77880 : ExecReScanMaterial((MaterialState *) node);
9914 bruce@momjian.us 255 : 77880 : break;
256 : :
1515 drowley@postgresql.o 257 : 354212 : case T_MemoizeState:
258 : 354212 : ExecReScanMemoize((MemoizeState *) node);
1618 259 : 354212 : break;
260 : :
8311 tgl@sss.pgh.pa.us 261 : 27114 : case T_SortState:
5535 262 : 27114 : ExecReScanSort((SortState *) node);
10067 vadim4o@yahoo.com 263 : 27114 : break;
264 : :
1979 tomas.vondra@postgre 265 : 6 : case T_IncrementalSortState:
266 : 6 : ExecReScanIncrementalSort((IncrementalSortState *) node);
267 : 6 : break;
268 : :
8311 tgl@sss.pgh.pa.us 269 : 12 : case T_GroupState:
5535 270 : 12 : ExecReScanGroup((GroupState *) node);
10057 vadim4o@yahoo.com 271 : 12 : break;
272 : :
8311 tgl@sss.pgh.pa.us 273 : 27209 : case T_AggState:
5535 274 : 27209 : ExecReScanAgg((AggState *) node);
9102 275 : 27209 : break;
276 : :
6096 277 : 39 : case T_WindowAggState:
5535 278 : 39 : ExecReScanWindowAgg((WindowAggState *) node);
6096 279 : 39 : break;
280 : :
8311 tgl@sss.pgh.pa.us 281 :GBC 18 : case T_UniqueState:
5535 282 : 18 : ExecReScanUnique((UniqueState *) node);
9081 283 : 18 : break;
284 : :
8311 tgl@sss.pgh.pa.us 285 :CBC 951 : case T_HashState:
5535 286 : 951 : ExecReScanHash((HashState *) node);
10057 vadim4o@yahoo.com 287 : 951 : break;
288 : :
8311 tgl@sss.pgh.pa.us 289 : 600 : case T_SetOpState:
5535 290 : 600 : ExecReScanSetOp((SetOpState *) node);
10053 vadim4o@yahoo.com 291 : 600 : break;
292 : :
5808 tgl@sss.pgh.pa.us 293 : 8 : case T_LockRowsState:
5535 294 : 8 : ExecReScanLockRows((LockRowsState *) node);
5808 295 : 8 : break;
296 : :
8311 297 : 30495 : case T_LimitState:
5535 298 : 30495 : ExecReScanLimit((LimitState *) node);
9915 bruce@momjian.us 299 : 30495 : break;
300 : :
10225 bruce@momjian.us 301 :UBC 0 : default:
8083 tgl@sss.pgh.pa.us 302 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
303 : : break;
304 : : }
305 : :
8245 tgl@sss.pgh.pa.us 306 [ + + ]:CBC 1629330 : if (node->chgParam != NULL)
307 : : {
308 : 1497074 : bms_free(node->chgParam);
309 : 1497074 : node->chgParam = NULL;
310 : : }
10651 scrappy@hub.org 311 : 1629330 : }
312 : :
313 : : /*
314 : : * ExecMarkPos
315 : : *
316 : : * Marks the current scan position.
317 : : *
318 : : * NOTE: mark/restore capability is currently needed only for plan nodes
319 : : * that are the immediate inner child of a MergeJoin node. Since MergeJoin
320 : : * requires sorted input, there is never any need to support mark/restore in
321 : : * node types that cannot produce sorted output. There are some cases in
322 : : * which a node can pass through sorted data from its child; if we don't
323 : : * implement mark/restore for such a node type, the planner compensates by
324 : : * inserting a Material node above that node.
325 : : */
326 : : void
8065 bruce@momjian.us 327 : 363134 : ExecMarkPos(PlanState *node)
328 : : {
10226 329 [ + + - + : 363134 : switch (nodeTag(node))
+ - - ]
330 : : {
8311 tgl@sss.pgh.pa.us 331 : 3030 : case T_IndexScanState:
332 : 3030 : ExecIndexMarkPos((IndexScanState *) node);
10225 bruce@momjian.us 333 : 3030 : break;
334 : :
5079 tgl@sss.pgh.pa.us 335 : 62019 : case T_IndexOnlyScanState:
336 : 62019 : ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
337 : 62019 : break;
338 : :
3956 rhaas@postgresql.org 339 :UBC 0 : case T_CustomScanState:
340 : 0 : ExecCustomMarkPos((CustomScanState *) node);
341 : 0 : break;
342 : :
8311 tgl@sss.pgh.pa.us 343 :CBC 3270 : case T_MaterialState:
344 : 3270 : ExecMaterialMarkPos((MaterialState *) node);
9211 345 : 3270 : break;
346 : :
8311 347 : 294815 : case T_SortState:
348 : 294815 : ExecSortMarkPos((SortState *) node);
10225 bruce@momjian.us 349 : 294815 : break;
350 : :
6778 tgl@sss.pgh.pa.us 351 :UBC 0 : case T_ResultState:
352 : 0 : ExecResultMarkPos((ResultState *) node);
353 : 0 : break;
354 : :
10225 bruce@momjian.us 355 : 0 : default:
356 : : /* don't make hard error unless caller asks to restore... */
8083 tgl@sss.pgh.pa.us 357 [ # # ]: 0 : elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
10225 bruce@momjian.us 358 : 0 : break;
359 : : }
10651 scrappy@hub.org 360 :CBC 363134 : }
361 : :
362 : : /*
363 : : * ExecRestrPos
364 : : *
365 : : * restores the scan position previously saved with ExecMarkPos()
366 : : *
367 : : * NOTE: the semantics of this are that the first ExecProcNode following
368 : : * the restore operation will yield the same tuple as the first one following
369 : : * the mark operation. It is unspecified what happens to the plan node's
370 : : * result TupleTableSlot. (In most cases the result slot is unchanged by
371 : : * a restore, but the node may choose to clear it or to load it with the
372 : : * restored-to tuple.) Hence the caller should discard any previously
373 : : * returned TupleTableSlot after doing a restore.
374 : : */
375 : : void
8065 bruce@momjian.us 376 : 70418 : ExecRestrPos(PlanState *node)
377 : : {
10226 378 [ + - - + : 70418 : switch (nodeTag(node))
+ - - ]
379 : : {
8311 tgl@sss.pgh.pa.us 380 : 27009 : case T_IndexScanState:
381 : 27009 : ExecIndexRestrPos((IndexScanState *) node);
9174 382 : 27009 : break;
383 : :
5079 tgl@sss.pgh.pa.us 384 :UBC 0 : case T_IndexOnlyScanState:
385 : 0 : ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
386 : 0 : break;
387 : :
3956 rhaas@postgresql.org 388 : 0 : case T_CustomScanState:
389 : 0 : ExecCustomRestrPos((CustomScanState *) node);
390 : 0 : break;
391 : :
8311 tgl@sss.pgh.pa.us 392 :CBC 27016 : case T_MaterialState:
393 : 27016 : ExecMaterialRestrPos((MaterialState *) node);
9174 394 : 27016 : break;
395 : :
8311 396 : 16393 : case T_SortState:
397 : 16393 : ExecSortRestrPos((SortState *) node);
9174 398 : 16393 : break;
399 : :
6778 tgl@sss.pgh.pa.us 400 :UBC 0 : case T_ResultState:
401 : 0 : ExecResultRestrPos((ResultState *) node);
402 : 0 : break;
403 : :
10225 bruce@momjian.us 404 : 0 : default:
8083 tgl@sss.pgh.pa.us 405 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
406 : : break;
407 : : }
10651 scrappy@hub.org 408 :CBC 70418 : }
409 : :
410 : : /*
411 : : * ExecSupportsMarkRestore - does a Path support mark/restore?
412 : : *
413 : : * This is used during planning and so must accept a Path, not a Plan.
414 : : * We keep it here to be adjacent to the routines above, which also must
415 : : * know which plan types support mark/restore.
416 : : */
417 : : bool
3956 rhaas@postgresql.org 418 : 4055 : ExecSupportsMarkRestore(Path *pathnode)
419 : : {
420 : : /*
421 : : * For consistency with the routines above, we do not examine the nodeTag
422 : : * but rather the pathtype, which is the Plan node type the Path would
423 : : * produce.
424 : : */
425 [ + - - - : 4055 : switch (pathnode->pathtype)
+ + + ]
426 : : {
8316 tgl@sss.pgh.pa.us 427 : 3139 : case T_IndexScan:
428 : : case T_IndexOnlyScan:
429 : :
430 : : /*
431 : : * Not all index types support mark/restore.
432 : : */
1747 rhodiumtoad@postgres 433 : 3139 : return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
434 : :
8316 tgl@sss.pgh.pa.us 435 :UBC 0 : case T_Material:
436 : : case T_Sort:
437 : 0 : return true;
438 : :
3943 439 : 0 : case T_CustomScan:
1523 440 [ # # ]: 0 : if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
441 : 0 : return true;
442 : 0 : return false;
443 : :
6778 444 : 0 : case T_Result:
445 : :
446 : : /*
447 : : * Result supports mark/restore iff it has a child plan that does.
448 : : *
449 : : * We have to be careful here because there is more than one Path
450 : : * type that can produce a Result plan node.
451 : : */
3470 452 [ # # ]: 0 : if (IsA(pathnode, ProjectionPath))
453 : 0 : return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath);
454 [ # # ]: 0 : else if (IsA(pathnode, MinMaxAggPath))
455 : 0 : return false; /* childless Result */
2413 456 [ # # ]: 0 : else if (IsA(pathnode, GroupResultPath))
457 : 0 : return false; /* childless Result */
458 : : else
459 : : {
460 : : /* Simple RTE_RESULT base relation */
461 [ # # ]: 0 : Assert(IsA(pathnode, Path));
3470 462 : 0 : return false; /* childless Result */
463 : : }
464 : :
2357 tgl@sss.pgh.pa.us 465 :CBC 42 : case T_Append:
466 : : {
467 : 42 : AppendPath *appendPath = castNode(AppendPath, pathnode);
468 : :
469 : : /*
470 : : * If there's exactly one child, then there will be no Append
471 : : * in the final plan, so we can handle mark/restore if the
472 : : * child plan node can.
473 : : */
474 [ - + ]: 42 : if (list_length(appendPath->subpaths) == 1)
2357 tgl@sss.pgh.pa.us 475 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
476 : : /* Otherwise, Append can't handle it */
2357 tgl@sss.pgh.pa.us 477 :CBC 42 : return false;
478 : : }
479 : :
480 : 22 : case T_MergeAppend:
481 : : {
482 : 22 : MergeAppendPath *mapath = castNode(MergeAppendPath, pathnode);
483 : :
484 : : /*
485 : : * Like the Append case above, single-subpath MergeAppends
486 : : * won't be in the final plan, so just return the child's
487 : : * mark/restore ability.
488 : : */
489 [ - + ]: 22 : if (list_length(mapath->subpaths) == 1)
2357 tgl@sss.pgh.pa.us 490 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
491 : : /* Otherwise, MergeAppend can't handle it */
2357 tgl@sss.pgh.pa.us 492 :CBC 22 : return false;
493 : : }
494 : :
8316 495 : 852 : default:
496 : 852 : break;
497 : : }
498 : :
499 : 852 : return false;
500 : : }
501 : :
502 : : /*
503 : : * ExecSupportsBackwardScan - does a plan type support backwards scanning?
504 : : *
505 : : * Ideally, all plan types would support backwards scan, but that seems
506 : : * unlikely to happen soon. In some cases, a plan node passes the backwards
507 : : * scan down to its children, and so supports backwards scan only if its
508 : : * children do. Therefore, this routine must be passed a complete plan tree.
509 : : */
510 : : bool
8216 511 : 2469 : ExecSupportsBackwardScan(Plan *node)
512 : : {
513 [ - + ]: 2469 : if (node == NULL)
8216 tgl@sss.pgh.pa.us 514 :UBC 0 : return false;
515 : :
516 : : /*
517 : : * Parallel-aware nodes return a subset of the tuples in each worker, and
518 : : * in general we can't expect to have enough bookkeeping state to know
519 : : * which ones we returned in this worker as opposed to some other worker.
520 : : */
3587 rhaas@postgresql.org 521 [ - + ]:CBC 2469 : if (node->parallel_aware)
3587 rhaas@postgresql.org 522 :UBC 0 : return false;
523 : :
8216 tgl@sss.pgh.pa.us 524 [ + + + - :CBC 2469 : switch (nodeTag(node))
+ + - - +
+ + + ]
525 : : {
526 : 35 : case T_Result:
527 [ - + ]: 35 : if (outerPlan(node) != NULL)
3152 andres@anarazel.de 528 :UBC 0 : return ExecSupportsBackwardScan(outerPlan(node));
529 : : else
8216 tgl@sss.pgh.pa.us 530 :CBC 35 : return false;
531 : :
532 : 20 : case T_Append:
533 : : {
534 : : ListCell *l;
535 : :
536 : : /* With async, tuples may be interleaved, so can't back up. */
1620 efujita@postgresql.o 537 [ - + ]: 20 : if (((Append *) node)->nasyncplans > 0)
1620 efujita@postgresql.o 538 :UBC 0 : return false;
539 : :
8069 bruce@momjian.us 540 [ + - + + :CBC 68 : foreach(l, ((Append *) node)->appendplans)
+ + ]
541 : : {
542 [ + + ]: 49 : if (!ExecSupportsBackwardScan((Plan *) lfirst(l)))
543 : 1 : return false;
544 : : }
545 : : /* need not check tlist because Append doesn't evaluate it */
546 : 19 : return true;
547 : : }
548 : :
3696 tgl@sss.pgh.pa.us 549 : 3 : case T_SampleScan:
550 : : /* Simplify life for tablesample methods by disallowing this */
551 : 3 : return false;
552 : :
3629 rhaas@postgresql.org 553 :UBC 0 : case T_Gather:
554 : 0 : return false;
555 : :
6168 tgl@sss.pgh.pa.us 556 :CBC 201 : case T_IndexScan:
3152 andres@anarazel.de 557 : 201 : return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
558 : :
5079 tgl@sss.pgh.pa.us 559 : 24 : case T_IndexOnlyScan:
3152 andres@anarazel.de 560 : 24 : return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
561 : :
8216 tgl@sss.pgh.pa.us 562 :UBC 0 : case T_SubqueryScan:
3152 andres@anarazel.de 563 : 0 : return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
564 : :
3956 rhaas@postgresql.org 565 : 0 : case T_CustomScan:
1523 tgl@sss.pgh.pa.us 566 [ # # ]: 0 : if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
567 : 0 : return true;
3956 rhaas@postgresql.org 568 : 0 : return false;
569 : :
3152 andres@anarazel.de 570 :CBC 1749 : case T_SeqScan:
571 : : case T_TidScan:
572 : : case T_TidRangeScan:
573 : : case T_FunctionScan:
574 : : case T_ValuesScan:
575 : : case T_CteScan:
576 : : case T_Material:
577 : : case T_Sort:
578 : : /* these don't evaluate tlist */
8216 tgl@sss.pgh.pa.us 579 : 1749 : return true;
580 : :
1979 tomas.vondra@postgre 581 : 2 : case T_IncrementalSort:
582 : :
583 : : /*
584 : : * Unlike full sort, incremental sort keeps only a single group of
585 : : * tuples in memory, so it can't scan backwards.
586 : : */
587 : 2 : return false;
588 : :
5808 tgl@sss.pgh.pa.us 589 : 74 : case T_LockRows:
590 : : case T_Limit:
8216 591 : 74 : return ExecSupportsBackwardScan(outerPlan(node));
592 : :
593 : 361 : default:
594 : 361 : return false;
595 : : }
596 : : }
597 : :
598 : : /*
599 : : * An IndexScan or IndexOnlyScan node supports backward scan only if the
600 : : * index's AM does.
601 : : */
602 : : static bool
6168 603 : 225 : IndexSupportsBackwardScan(Oid indexid)
604 : : {
605 : : bool result;
606 : : HeapTuple ht_idxrel;
607 : : Form_pg_class idxrelrec;
608 : : IndexAmRoutine *amroutine;
609 : :
610 : : /* Fetch the pg_class tuple of the index relation */
5683 rhaas@postgresql.org 611 : 225 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
6168 tgl@sss.pgh.pa.us 612 [ - + ]: 225 : if (!HeapTupleIsValid(ht_idxrel))
6168 tgl@sss.pgh.pa.us 613 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", indexid);
6168 tgl@sss.pgh.pa.us 614 :CBC 225 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
615 : :
616 : : /* Fetch the index AM's API struct */
3311 617 : 225 : amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
618 : :
3520 619 : 225 : result = amroutine->amcanbackward;
620 : :
621 : 225 : pfree(amroutine);
6168 622 : 225 : ReleaseSysCache(ht_idxrel);
623 : :
624 : 225 : 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
5838 636 : 273903 : ExecMaterializesOutput(NodeTag plantype)
637 : : {
638 [ + + ]: 273903 : switch (plantype)
639 : : {
640 : 13483 : 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 : 13483 : return true;
648 : :
649 : 260420 : default:
650 : 260420 : break;
651 : : }
652 : :
653 : 260420 : return false;
654 : : }
|