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
5636 tgl@sss.pgh.pa.us 77 :CBC 1723638 : ExecReScan(PlanState *node)
78 : : {
79 : : /* If collecting timing stats, update them */
8855 80 [ + + ]: 1723638 : if (node->instrument)
81 : 21458 : 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 : : */
8346 96 [ + + ]: 1723638 : if (node->chgParam != NULL)
97 : : {
98 : : ListCell *l;
99 : :
7874 neilc@samurai.com 100 [ + + + + : 1587502 : foreach(l, node->initPlan)
+ + ]
101 : : {
102 : 884 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8404 tgl@sss.pgh.pa.us 103 : 884 : PlanState *splan = sstate->planstate;
104 : :
8346 105 [ + + ]: 884 : if (splan->plan->extParam != NULL) /* don't care about child
106 : : * local Params */
107 : 811 : UpdateChangedParamSet(splan, node->chgParam);
108 [ + + ]: 884 : if (splan->chgParam != NULL)
8404 109 : 691 : ExecReScanSetParamPlan(sstate, node);
110 : : }
7874 neilc@samurai.com 111 [ + + + + : 1587048 : foreach(l, node->subPlan)
+ + ]
112 : : {
113 : 430 : SubPlanState *sstate = (SubPlanState *) lfirst(l);
8404 tgl@sss.pgh.pa.us 114 : 430 : PlanState *splan = sstate->planstate;
115 : :
8346 116 [ + + ]: 430 : if (splan->plan->extParam != NULL)
117 : 427 : UpdateChangedParamSet(splan, node->chgParam);
118 : : }
119 : : /* Well. Now set chgParam for child trees. */
1258 120 [ + + ]: 1586618 : if (outerPlanState(node) != NULL)
121 : 517673 : UpdateChangedParamSet(outerPlanState(node), node->chgParam);
122 [ + + ]: 1586618 : if (innerPlanState(node) != NULL)
123 : 9086 : UpdateChangedParamSet(innerPlanState(node), node->chgParam);
124 : : }
125 : :
126 : : /* Call expression callbacks */
8034 127 [ + + ]: 1723638 : if (node->ps_ExprContext)
128 : 1606114 : ReScanExprContext(node->ps_ExprContext);
129 : :
130 : : /* And do node-type-specific processing */
10327 bruce@momjian.us 131 [ + + - + : 1723638 : switch (nodeTag(node))
+ + + + +
+ + + + +
+ + + + +
+ + + + -
+ + - + +
+ + + + +
+ + + + +
+ + + - ]
132 : : {
8412 tgl@sss.pgh.pa.us 133 : 23866 : case T_ResultState:
5636 134 : 23866 : ExecReScanResult((ResultState *) node);
10168 vadim4o@yahoo.com 135 : 23866 : break;
136 : :
3254 andres@anarazel.de 137 : 9101 : case T_ProjectSetState:
138 : 9101 : ExecReScanProjectSet((ProjectSetState *) node);
139 : 9101 : break;
140 : :
5911 tgl@sss.pgh.pa.us 141 :UBC 0 : case T_ModifyTableState:
5636 142 : 0 : ExecReScanModifyTable((ModifyTableState *) node);
5911 143 : 0 : break;
144 : :
8412 tgl@sss.pgh.pa.us 145 :CBC 11216 : case T_AppendState:
5636 146 : 11216 : ExecReScanAppend((AppendState *) node);
10168 vadim4o@yahoo.com 147 : 11216 : break;
148 : :
5542 tgl@sss.pgh.pa.us 149 : 9 : case T_MergeAppendState:
150 : 9 : ExecReScanMergeAppend((MergeAppendState *) node);
151 : 9 : break;
152 : :
6282 153 : 6 : case T_RecursiveUnionState:
5636 154 : 6 : ExecReScanRecursiveUnion((RecursiveUnionState *) node);
6282 155 : 6 : break;
156 : :
7546 157 : 77 : case T_BitmapAndState:
5636 158 : 77 : ExecReScanBitmapAnd((BitmapAndState *) node);
7546 159 : 77 : break;
160 : :
161 : 20 : case T_BitmapOrState:
5636 162 : 20 : ExecReScanBitmapOr((BitmapOrState *) node);
7546 163 : 20 : break;
164 : :
8412 165 : 643029 : case T_SeqScanState:
5636 166 : 643029 : ExecReScanSeqScan((SeqScanState *) node);
9209 167 : 643029 : break;
168 : :
3868 simon@2ndQuadrant.co 169 : 29 : case T_SampleScanState:
170 : 29 : ExecReScanSampleScan((SampleScanState *) node);
171 : 29 : break;
172 : :
3730 rhaas@postgresql.org 173 : 150 : case T_GatherState:
174 : 150 : ExecReScanGather((GatherState *) node);
175 : 150 : break;
176 : :
3045 177 : 24 : case T_GatherMergeState:
178 : 24 : ExecReScanGatherMerge((GatherMergeState *) node);
179 : 24 : break;
180 : :
8412 tgl@sss.pgh.pa.us 181 : 232873 : case T_IndexScanState:
5636 182 : 232873 : ExecReScanIndexScan((IndexScanState *) node);
9209 183 : 232873 : break;
184 : :
5180 185 : 110893 : case T_IndexOnlyScanState:
186 : 110893 : ExecReScanIndexOnlyScan((IndexOnlyScanState *) node);
187 : 110893 : break;
188 : :
7546 189 : 2839 : case T_BitmapIndexScanState:
5636 190 : 2839 : ExecReScanBitmapIndexScan((BitmapIndexScanState *) node);
7546 191 : 2839 : break;
192 : :
193 : 2521 : case T_BitmapHeapScanState:
5636 194 : 2521 : ExecReScanBitmapHeapScan((BitmapHeapScanState *) node);
7546 195 : 2521 : break;
196 : :
8412 197 : 9 : case T_TidScanState:
5636 198 : 9 : ExecReScanTidScan((TidScanState *) node);
8619 199 : 9 : break;
200 : :
1753 drowley@postgresql.o 201 : 48 : case T_TidRangeScanState:
202 : 48 : ExecReScanTidRangeScan((TidRangeScanState *) node);
203 : 48 : break;
204 : :
8412 tgl@sss.pgh.pa.us 205 : 229 : case T_SubqueryScanState:
5636 206 : 229 : ExecReScanSubqueryScan((SubqueryScanState *) node);
10168 vadim4o@yahoo.com 207 : 229 : break;
208 : :
8412 tgl@sss.pgh.pa.us 209 : 67302 : case T_FunctionScanState:
5636 210 : 67302 : ExecReScanFunctionScan((FunctionScanState *) node);
10168 vadim4o@yahoo.com 211 : 67302 : break;
212 : :
3205 alvherre@alvh.no-ip. 213 : 222 : case T_TableFuncScanState:
214 : 222 : ExecReScanTableFuncScan((TableFuncScanState *) node);
215 : 222 : break;
216 : :
7076 mail@joeconway.com 217 : 30189 : case T_ValuesScanState:
5636 tgl@sss.pgh.pa.us 218 : 30189 : ExecReScanValuesScan((ValuesScanState *) node);
7076 mail@joeconway.com 219 : 30189 : break;
220 : :
6282 tgl@sss.pgh.pa.us 221 : 3136 : case T_CteScanState:
5636 222 : 3136 : ExecReScanCteScan((CteScanState *) node);
6282 223 : 3136 : break;
224 : :
3182 kgrittn@postgresql.o 225 :UBC 0 : case T_NamedTuplestoreScanState:
226 : 0 : ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node);
227 : 0 : break;
228 : :
6282 tgl@sss.pgh.pa.us 229 :CBC 3290 : case T_WorkTableScanState:
5636 230 : 3290 : ExecReScanWorkTableScan((WorkTableScanState *) node);
6282 231 : 3290 : break;
232 : :
5413 233 : 410 : case T_ForeignScanState:
234 : 410 : ExecReScanForeignScan((ForeignScanState *) node);
235 : 410 : break;
236 : :
4057 rhaas@postgresql.org 237 :UBC 0 : case T_CustomScanState:
238 : 0 : ExecReScanCustomScan((CustomScanState *) node);
239 : 0 : break;
240 : :
8412 tgl@sss.pgh.pa.us 241 :CBC 6869 : case T_NestLoopState:
5636 242 : 6869 : ExecReScanNestLoop((NestLoopState *) node);
10168 vadim4o@yahoo.com 243 : 6869 : break;
244 : :
8412 tgl@sss.pgh.pa.us 245 : 382 : case T_MergeJoinState:
5636 246 : 382 : ExecReScanMergeJoin((MergeJoinState *) node);
10168 vadim4o@yahoo.com 247 : 382 : break;
248 : :
8412 tgl@sss.pgh.pa.us 249 : 2019 : case T_HashJoinState:
5636 250 : 2019 : ExecReScanHashJoin((HashJoinState *) node);
10168 vadim4o@yahoo.com 251 : 2019 : break;
252 : :
8412 tgl@sss.pgh.pa.us 253 : 76798 : case T_MaterialState:
5636 254 : 76798 : ExecReScanMaterial((MaterialState *) node);
10015 bruce@momjian.us 255 : 76798 : break;
256 : :
1616 drowley@postgresql.o 257 : 410067 : case T_MemoizeState:
258 : 410067 : ExecReScanMemoize((MemoizeState *) node);
1719 259 : 410067 : break;
260 : :
8412 tgl@sss.pgh.pa.us 261 : 26545 : case T_SortState:
5636 262 : 26545 : ExecReScanSort((SortState *) node);
10168 vadim4o@yahoo.com 263 : 26545 : break;
264 : :
2080 tomas.vondra@postgre 265 : 6 : case T_IncrementalSortState:
266 : 6 : ExecReScanIncrementalSort((IncrementalSortState *) node);
267 : 6 : break;
268 : :
8412 tgl@sss.pgh.pa.us 269 : 12 : case T_GroupState:
5636 270 : 12 : ExecReScanGroup((GroupState *) node);
10158 vadim4o@yahoo.com 271 : 12 : break;
272 : :
8412 tgl@sss.pgh.pa.us 273 : 27346 : case T_AggState:
5636 274 : 27346 : ExecReScanAgg((AggState *) node);
9203 275 : 27346 : break;
276 : :
6197 277 : 39 : case T_WindowAggState:
5636 278 : 39 : ExecReScanWindowAgg((WindowAggState *) node);
6197 279 : 39 : break;
280 : :
8412 tgl@sss.pgh.pa.us 281 :GBC 18 : case T_UniqueState:
5636 282 : 18 : ExecReScanUnique((UniqueState *) node);
9182 283 : 18 : break;
284 : :
8412 tgl@sss.pgh.pa.us 285 :CBC 933 : case T_HashState:
5636 286 : 933 : ExecReScanHash((HashState *) node);
10158 vadim4o@yahoo.com 287 : 933 : break;
288 : :
8412 tgl@sss.pgh.pa.us 289 : 600 : case T_SetOpState:
5636 290 : 600 : ExecReScanSetOp((SetOpState *) node);
10154 vadim4o@yahoo.com 291 : 600 : break;
292 : :
5909 tgl@sss.pgh.pa.us 293 : 8 : case T_LockRowsState:
5636 294 : 8 : ExecReScanLockRows((LockRowsState *) node);
5909 295 : 8 : break;
296 : :
8412 297 : 30508 : case T_LimitState:
5636 298 : 30508 : ExecReScanLimit((LimitState *) node);
10016 bruce@momjian.us 299 : 30508 : break;
300 : :
10326 bruce@momjian.us 301 :UBC 0 : default:
8184 tgl@sss.pgh.pa.us 302 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
303 : : break;
304 : : }
305 : :
8346 tgl@sss.pgh.pa.us 306 [ + + ]:CBC 1723638 : if (node->chgParam != NULL)
307 : : {
308 : 1586618 : bms_free(node->chgParam);
309 : 1586618 : node->chgParam = NULL;
310 : : }
10752 scrappy@hub.org 311 : 1723638 : }
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
8166 bruce@momjian.us 327 : 364003 : ExecMarkPos(PlanState *node)
328 : : {
10327 329 [ + + - + : 364003 : switch (nodeTag(node))
+ - - ]
330 : : {
8412 tgl@sss.pgh.pa.us 331 : 3038 : case T_IndexScanState:
332 : 3038 : ExecIndexMarkPos((IndexScanState *) node);
10326 bruce@momjian.us 333 : 3038 : break;
334 : :
5180 tgl@sss.pgh.pa.us 335 : 62019 : case T_IndexOnlyScanState:
336 : 62019 : ExecIndexOnlyMarkPos((IndexOnlyScanState *) node);
337 : 62019 : break;
338 : :
4057 rhaas@postgresql.org 339 :UBC 0 : case T_CustomScanState:
340 : 0 : ExecCustomMarkPos((CustomScanState *) node);
341 : 0 : break;
342 : :
8412 tgl@sss.pgh.pa.us 343 :CBC 3270 : case T_MaterialState:
344 : 3270 : ExecMaterialMarkPos((MaterialState *) node);
9312 345 : 3270 : break;
346 : :
8412 347 : 295676 : case T_SortState:
348 : 295676 : ExecSortMarkPos((SortState *) node);
10326 bruce@momjian.us 349 : 295676 : break;
350 : :
6879 tgl@sss.pgh.pa.us 351 :UBC 0 : case T_ResultState:
352 : 0 : ExecResultMarkPos((ResultState *) node);
353 : 0 : break;
354 : :
10326 bruce@momjian.us 355 : 0 : default:
356 : : /* don't make hard error unless caller asks to restore... */
8184 tgl@sss.pgh.pa.us 357 [ # # ]: 0 : elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node));
10326 bruce@momjian.us 358 : 0 : break;
359 : : }
10752 scrappy@hub.org 360 :CBC 364003 : }
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
8166 bruce@momjian.us 376 : 73385 : ExecRestrPos(PlanState *node)
377 : : {
10327 378 [ + - - + : 73385 : switch (nodeTag(node))
+ - - ]
379 : : {
8412 tgl@sss.pgh.pa.us 380 : 27015 : case T_IndexScanState:
381 : 27015 : ExecIndexRestrPos((IndexScanState *) node);
9275 382 : 27015 : break;
383 : :
5180 tgl@sss.pgh.pa.us 384 :UBC 0 : case T_IndexOnlyScanState:
385 : 0 : ExecIndexOnlyRestrPos((IndexOnlyScanState *) node);
386 : 0 : break;
387 : :
4057 rhaas@postgresql.org 388 : 0 : case T_CustomScanState:
389 : 0 : ExecCustomRestrPos((CustomScanState *) node);
390 : 0 : break;
391 : :
8412 tgl@sss.pgh.pa.us 392 :CBC 27016 : case T_MaterialState:
393 : 27016 : ExecMaterialRestrPos((MaterialState *) node);
9275 394 : 27016 : break;
395 : :
8412 396 : 19354 : case T_SortState:
397 : 19354 : ExecSortRestrPos((SortState *) node);
9275 398 : 19354 : break;
399 : :
6879 tgl@sss.pgh.pa.us 400 :UBC 0 : case T_ResultState:
401 : 0 : ExecResultRestrPos((ResultState *) node);
402 : 0 : break;
403 : :
10326 bruce@momjian.us 404 : 0 : default:
8184 tgl@sss.pgh.pa.us 405 [ # # ]: 0 : elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
406 : : break;
407 : : }
10752 scrappy@hub.org 408 :CBC 73385 : }
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
4057 rhaas@postgresql.org 418 : 4505 : 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 [ + - - - : 4505 : switch (pathnode->pathtype)
+ + + ]
426 : : {
8417 tgl@sss.pgh.pa.us 427 : 3543 : case T_IndexScan:
428 : : case T_IndexOnlyScan:
429 : :
430 : : /*
431 : : * Not all index types support mark/restore.
432 : : */
1848 rhodiumtoad@postgres 433 : 3543 : return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos;
434 : :
8417 tgl@sss.pgh.pa.us 435 :UBC 0 : case T_Material:
436 : : case T_Sort:
437 : 0 : return true;
438 : :
4044 439 : 0 : case T_CustomScan:
1624 440 [ # # ]: 0 : if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE)
441 : 0 : return true;
442 : 0 : return false;
443 : :
6879 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 : : */
3571 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 */
2514 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));
3571 462 : 0 : return false; /* childless Result */
463 : : }
464 : :
2458 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)
2458 tgl@sss.pgh.pa.us 475 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths));
476 : : /* Otherwise, Append can't handle it */
2458 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)
2458 tgl@sss.pgh.pa.us 490 :UBC 0 : return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths));
491 : : /* Otherwise, MergeAppend can't handle it */
2458 tgl@sss.pgh.pa.us 492 :CBC 22 : return false;
493 : : }
494 : :
8417 495 : 898 : default:
496 : 898 : break;
497 : : }
498 : :
499 : 898 : 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
8317 511 : 2474 : ExecSupportsBackwardScan(Plan *node)
512 : : {
513 [ - + ]: 2474 : if (node == NULL)
8317 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 : : */
3688 rhaas@postgresql.org 521 [ - + ]:CBC 2474 : if (node->parallel_aware)
3688 rhaas@postgresql.org 522 :UBC 0 : return false;
523 : :
8317 tgl@sss.pgh.pa.us 524 [ + + + - :CBC 2474 : switch (nodeTag(node))
+ + - - +
+ + + ]
525 : : {
526 : 35 : case T_Result:
527 [ - + ]: 35 : if (outerPlan(node) != NULL)
3253 andres@anarazel.de 528 :UBC 0 : return ExecSupportsBackwardScan(outerPlan(node));
529 : : else
8317 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. */
1721 efujita@postgresql.o 537 [ - + ]: 20 : if (((Append *) node)->nasyncplans > 0)
1721 efujita@postgresql.o 538 :UBC 0 : return false;
539 : :
8170 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 : :
3797 tgl@sss.pgh.pa.us 549 : 3 : case T_SampleScan:
550 : : /* Simplify life for tablesample methods by disallowing this */
551 : 3 : return false;
552 : :
3730 rhaas@postgresql.org 553 :UBC 0 : case T_Gather:
554 : 0 : return false;
555 : :
6269 tgl@sss.pgh.pa.us 556 :CBC 203 : case T_IndexScan:
3253 andres@anarazel.de 557 : 203 : return IndexSupportsBackwardScan(((IndexScan *) node)->indexid);
558 : :
5180 tgl@sss.pgh.pa.us 559 : 24 : case T_IndexOnlyScan:
3253 andres@anarazel.de 560 : 24 : return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid);
561 : :
8317 tgl@sss.pgh.pa.us 562 :UBC 0 : case T_SubqueryScan:
3253 andres@anarazel.de 563 : 0 : return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan);
564 : :
4057 rhaas@postgresql.org 565 : 0 : case T_CustomScan:
1624 tgl@sss.pgh.pa.us 566 [ # # ]: 0 : if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN)
567 : 0 : return true;
4057 rhaas@postgresql.org 568 : 0 : return false;
569 : :
3253 andres@anarazel.de 570 :CBC 1747 : 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 */
8317 tgl@sss.pgh.pa.us 579 : 1747 : return true;
580 : :
2080 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 : :
5909 tgl@sss.pgh.pa.us 589 : 76 : case T_LockRows:
590 : : case T_Limit:
8317 591 : 76 : return ExecSupportsBackwardScan(outerPlan(node));
592 : :
593 : 364 : default:
594 : 364 : 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
6269 603 : 227 : 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 */
5784 rhaas@postgresql.org 611 : 227 : ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid));
6269 tgl@sss.pgh.pa.us 612 [ - + ]: 227 : if (!HeapTupleIsValid(ht_idxrel))
6269 tgl@sss.pgh.pa.us 613 [ # # ]:UBC 0 : elog(ERROR, "cache lookup failed for relation %u", indexid);
6269 tgl@sss.pgh.pa.us 614 :CBC 227 : idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
615 : :
616 : : /* Fetch the index AM's API struct */
3412 617 : 227 : amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false);
618 : :
3621 619 : 227 : result = amroutine->amcanbackward;
620 : :
621 : 227 : pfree(amroutine);
6269 622 : 227 : ReleaseSysCache(ht_idxrel);
623 : :
624 : 227 : 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
5939 636 : 361913 : ExecMaterializesOutput(NodeTag plantype)
637 : : {
638 [ + + ]: 361913 : switch (plantype)
639 : : {
640 : 14217 : 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 : 14217 : return true;
648 : :
649 : 347696 : default:
650 : 347696 : break;
651 : : }
652 : :
653 : 347696 : return false;
654 : : }
|