/src/postgres/src/backend/executor/execAmi.c
Line | Count | Source (jump to first uncovered line) |
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 |
77 | | ExecReScan(PlanState *node) |
78 | 0 | { |
79 | | /* If collecting timing stats, update them */ |
80 | 0 | if (node->instrument) |
81 | 0 | 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 | | */ |
96 | 0 | if (node->chgParam != NULL) |
97 | 0 | { |
98 | 0 | ListCell *l; |
99 | |
|
100 | 0 | foreach(l, node->initPlan) |
101 | 0 | { |
102 | 0 | SubPlanState *sstate = (SubPlanState *) lfirst(l); |
103 | 0 | PlanState *splan = sstate->planstate; |
104 | |
|
105 | 0 | if (splan->plan->extParam != NULL) /* don't care about child |
106 | | * local Params */ |
107 | 0 | UpdateChangedParamSet(splan, node->chgParam); |
108 | 0 | if (splan->chgParam != NULL) |
109 | 0 | ExecReScanSetParamPlan(sstate, node); |
110 | 0 | } |
111 | 0 | foreach(l, node->subPlan) |
112 | 0 | { |
113 | 0 | SubPlanState *sstate = (SubPlanState *) lfirst(l); |
114 | 0 | PlanState *splan = sstate->planstate; |
115 | |
|
116 | 0 | if (splan->plan->extParam != NULL) |
117 | 0 | UpdateChangedParamSet(splan, node->chgParam); |
118 | 0 | } |
119 | | /* Well. Now set chgParam for child trees. */ |
120 | 0 | if (outerPlanState(node) != NULL) |
121 | 0 | UpdateChangedParamSet(outerPlanState(node), node->chgParam); |
122 | 0 | if (innerPlanState(node) != NULL) |
123 | 0 | UpdateChangedParamSet(innerPlanState(node), node->chgParam); |
124 | 0 | } |
125 | | |
126 | | /* Call expression callbacks */ |
127 | 0 | if (node->ps_ExprContext) |
128 | 0 | ReScanExprContext(node->ps_ExprContext); |
129 | | |
130 | | /* And do node-type-specific processing */ |
131 | 0 | switch (nodeTag(node)) |
132 | 0 | { |
133 | 0 | case T_ResultState: |
134 | 0 | ExecReScanResult((ResultState *) node); |
135 | 0 | break; |
136 | | |
137 | 0 | case T_ProjectSetState: |
138 | 0 | ExecReScanProjectSet((ProjectSetState *) node); |
139 | 0 | break; |
140 | | |
141 | 0 | case T_ModifyTableState: |
142 | 0 | ExecReScanModifyTable((ModifyTableState *) node); |
143 | 0 | break; |
144 | | |
145 | 0 | case T_AppendState: |
146 | 0 | ExecReScanAppend((AppendState *) node); |
147 | 0 | break; |
148 | | |
149 | 0 | case T_MergeAppendState: |
150 | 0 | ExecReScanMergeAppend((MergeAppendState *) node); |
151 | 0 | break; |
152 | | |
153 | 0 | case T_RecursiveUnionState: |
154 | 0 | ExecReScanRecursiveUnion((RecursiveUnionState *) node); |
155 | 0 | break; |
156 | | |
157 | 0 | case T_BitmapAndState: |
158 | 0 | ExecReScanBitmapAnd((BitmapAndState *) node); |
159 | 0 | break; |
160 | | |
161 | 0 | case T_BitmapOrState: |
162 | 0 | ExecReScanBitmapOr((BitmapOrState *) node); |
163 | 0 | break; |
164 | | |
165 | 0 | case T_SeqScanState: |
166 | 0 | ExecReScanSeqScan((SeqScanState *) node); |
167 | 0 | break; |
168 | | |
169 | 0 | case T_SampleScanState: |
170 | 0 | ExecReScanSampleScan((SampleScanState *) node); |
171 | 0 | break; |
172 | | |
173 | 0 | case T_GatherState: |
174 | 0 | ExecReScanGather((GatherState *) node); |
175 | 0 | break; |
176 | | |
177 | 0 | case T_GatherMergeState: |
178 | 0 | ExecReScanGatherMerge((GatherMergeState *) node); |
179 | 0 | break; |
180 | | |
181 | 0 | case T_IndexScanState: |
182 | 0 | ExecReScanIndexScan((IndexScanState *) node); |
183 | 0 | break; |
184 | | |
185 | 0 | case T_IndexOnlyScanState: |
186 | 0 | ExecReScanIndexOnlyScan((IndexOnlyScanState *) node); |
187 | 0 | break; |
188 | | |
189 | 0 | case T_BitmapIndexScanState: |
190 | 0 | ExecReScanBitmapIndexScan((BitmapIndexScanState *) node); |
191 | 0 | break; |
192 | | |
193 | 0 | case T_BitmapHeapScanState: |
194 | 0 | ExecReScanBitmapHeapScan((BitmapHeapScanState *) node); |
195 | 0 | break; |
196 | | |
197 | 0 | case T_TidScanState: |
198 | 0 | ExecReScanTidScan((TidScanState *) node); |
199 | 0 | break; |
200 | | |
201 | 0 | case T_TidRangeScanState: |
202 | 0 | ExecReScanTidRangeScan((TidRangeScanState *) node); |
203 | 0 | break; |
204 | | |
205 | 0 | case T_SubqueryScanState: |
206 | 0 | ExecReScanSubqueryScan((SubqueryScanState *) node); |
207 | 0 | break; |
208 | | |
209 | 0 | case T_FunctionScanState: |
210 | 0 | ExecReScanFunctionScan((FunctionScanState *) node); |
211 | 0 | break; |
212 | | |
213 | 0 | case T_TableFuncScanState: |
214 | 0 | ExecReScanTableFuncScan((TableFuncScanState *) node); |
215 | 0 | break; |
216 | | |
217 | 0 | case T_ValuesScanState: |
218 | 0 | ExecReScanValuesScan((ValuesScanState *) node); |
219 | 0 | break; |
220 | | |
221 | 0 | case T_CteScanState: |
222 | 0 | ExecReScanCteScan((CteScanState *) node); |
223 | 0 | break; |
224 | | |
225 | 0 | case T_NamedTuplestoreScanState: |
226 | 0 | ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node); |
227 | 0 | break; |
228 | | |
229 | 0 | case T_WorkTableScanState: |
230 | 0 | ExecReScanWorkTableScan((WorkTableScanState *) node); |
231 | 0 | break; |
232 | | |
233 | 0 | case T_ForeignScanState: |
234 | 0 | ExecReScanForeignScan((ForeignScanState *) node); |
235 | 0 | break; |
236 | | |
237 | 0 | case T_CustomScanState: |
238 | 0 | ExecReScanCustomScan((CustomScanState *) node); |
239 | 0 | break; |
240 | | |
241 | 0 | case T_NestLoopState: |
242 | 0 | ExecReScanNestLoop((NestLoopState *) node); |
243 | 0 | break; |
244 | | |
245 | 0 | case T_MergeJoinState: |
246 | 0 | ExecReScanMergeJoin((MergeJoinState *) node); |
247 | 0 | break; |
248 | | |
249 | 0 | case T_HashJoinState: |
250 | 0 | ExecReScanHashJoin((HashJoinState *) node); |
251 | 0 | break; |
252 | | |
253 | 0 | case T_MaterialState: |
254 | 0 | ExecReScanMaterial((MaterialState *) node); |
255 | 0 | break; |
256 | | |
257 | 0 | case T_MemoizeState: |
258 | 0 | ExecReScanMemoize((MemoizeState *) node); |
259 | 0 | break; |
260 | | |
261 | 0 | case T_SortState: |
262 | 0 | ExecReScanSort((SortState *) node); |
263 | 0 | break; |
264 | | |
265 | 0 | case T_IncrementalSortState: |
266 | 0 | ExecReScanIncrementalSort((IncrementalSortState *) node); |
267 | 0 | break; |
268 | | |
269 | 0 | case T_GroupState: |
270 | 0 | ExecReScanGroup((GroupState *) node); |
271 | 0 | break; |
272 | | |
273 | 0 | case T_AggState: |
274 | 0 | ExecReScanAgg((AggState *) node); |
275 | 0 | break; |
276 | | |
277 | 0 | case T_WindowAggState: |
278 | 0 | ExecReScanWindowAgg((WindowAggState *) node); |
279 | 0 | break; |
280 | | |
281 | 0 | case T_UniqueState: |
282 | 0 | ExecReScanUnique((UniqueState *) node); |
283 | 0 | break; |
284 | | |
285 | 0 | case T_HashState: |
286 | 0 | ExecReScanHash((HashState *) node); |
287 | 0 | break; |
288 | | |
289 | 0 | case T_SetOpState: |
290 | 0 | ExecReScanSetOp((SetOpState *) node); |
291 | 0 | break; |
292 | | |
293 | 0 | case T_LockRowsState: |
294 | 0 | ExecReScanLockRows((LockRowsState *) node); |
295 | 0 | break; |
296 | | |
297 | 0 | case T_LimitState: |
298 | 0 | ExecReScanLimit((LimitState *) node); |
299 | 0 | break; |
300 | | |
301 | 0 | default: |
302 | 0 | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); |
303 | 0 | break; |
304 | 0 | } |
305 | | |
306 | 0 | if (node->chgParam != NULL) |
307 | 0 | { |
308 | 0 | bms_free(node->chgParam); |
309 | 0 | node->chgParam = NULL; |
310 | 0 | } |
311 | 0 | } |
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 |
327 | | ExecMarkPos(PlanState *node) |
328 | 0 | { |
329 | 0 | switch (nodeTag(node)) |
330 | 0 | { |
331 | 0 | case T_IndexScanState: |
332 | 0 | ExecIndexMarkPos((IndexScanState *) node); |
333 | 0 | break; |
334 | | |
335 | 0 | case T_IndexOnlyScanState: |
336 | 0 | ExecIndexOnlyMarkPos((IndexOnlyScanState *) node); |
337 | 0 | break; |
338 | | |
339 | 0 | case T_CustomScanState: |
340 | 0 | ExecCustomMarkPos((CustomScanState *) node); |
341 | 0 | break; |
342 | | |
343 | 0 | case T_MaterialState: |
344 | 0 | ExecMaterialMarkPos((MaterialState *) node); |
345 | 0 | break; |
346 | | |
347 | 0 | case T_SortState: |
348 | 0 | ExecSortMarkPos((SortState *) node); |
349 | 0 | break; |
350 | | |
351 | 0 | case T_ResultState: |
352 | 0 | ExecResultMarkPos((ResultState *) node); |
353 | 0 | break; |
354 | | |
355 | 0 | default: |
356 | | /* don't make hard error unless caller asks to restore... */ |
357 | 0 | elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node)); |
358 | 0 | break; |
359 | 0 | } |
360 | 0 | } |
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 |
376 | | ExecRestrPos(PlanState *node) |
377 | 0 | { |
378 | 0 | switch (nodeTag(node)) |
379 | 0 | { |
380 | 0 | case T_IndexScanState: |
381 | 0 | ExecIndexRestrPos((IndexScanState *) node); |
382 | 0 | break; |
383 | | |
384 | 0 | case T_IndexOnlyScanState: |
385 | 0 | ExecIndexOnlyRestrPos((IndexOnlyScanState *) node); |
386 | 0 | break; |
387 | | |
388 | 0 | case T_CustomScanState: |
389 | 0 | ExecCustomRestrPos((CustomScanState *) node); |
390 | 0 | break; |
391 | | |
392 | 0 | case T_MaterialState: |
393 | 0 | ExecMaterialRestrPos((MaterialState *) node); |
394 | 0 | break; |
395 | | |
396 | 0 | case T_SortState: |
397 | 0 | ExecSortRestrPos((SortState *) node); |
398 | 0 | break; |
399 | | |
400 | 0 | case T_ResultState: |
401 | 0 | ExecResultRestrPos((ResultState *) node); |
402 | 0 | break; |
403 | | |
404 | 0 | default: |
405 | 0 | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); |
406 | 0 | break; |
407 | 0 | } |
408 | 0 | } |
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 |
418 | | ExecSupportsMarkRestore(Path *pathnode) |
419 | 0 | { |
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 | 0 | switch (pathnode->pathtype) |
426 | 0 | { |
427 | 0 | case T_IndexScan: |
428 | 0 | case T_IndexOnlyScan: |
429 | | |
430 | | /* |
431 | | * Not all index types support mark/restore. |
432 | | */ |
433 | 0 | return castNode(IndexPath, pathnode)->indexinfo->amcanmarkpos; |
434 | | |
435 | 0 | case T_Material: |
436 | 0 | case T_Sort: |
437 | 0 | return true; |
438 | | |
439 | 0 | case T_CustomScan: |
440 | 0 | if (castNode(CustomPath, pathnode)->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE) |
441 | 0 | return true; |
442 | 0 | return false; |
443 | | |
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 | | */ |
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 */ |
456 | 0 | else if (IsA(pathnode, GroupResultPath)) |
457 | 0 | return false; /* childless Result */ |
458 | 0 | else |
459 | 0 | { |
460 | | /* Simple RTE_RESULT base relation */ |
461 | 0 | Assert(IsA(pathnode, Path)); |
462 | 0 | return false; /* childless Result */ |
463 | 0 | } |
464 | | |
465 | 0 | case T_Append: |
466 | 0 | { |
467 | 0 | 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 | 0 | if (list_length(appendPath->subpaths) == 1) |
475 | 0 | return ExecSupportsMarkRestore((Path *) linitial(appendPath->subpaths)); |
476 | | /* Otherwise, Append can't handle it */ |
477 | 0 | return false; |
478 | 0 | } |
479 | | |
480 | 0 | case T_MergeAppend: |
481 | 0 | { |
482 | 0 | 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 | 0 | if (list_length(mapath->subpaths) == 1) |
490 | 0 | return ExecSupportsMarkRestore((Path *) linitial(mapath->subpaths)); |
491 | | /* Otherwise, MergeAppend can't handle it */ |
492 | 0 | return false; |
493 | 0 | } |
494 | | |
495 | 0 | default: |
496 | 0 | break; |
497 | 0 | } |
498 | | |
499 | 0 | return false; |
500 | 0 | } |
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 |
511 | | ExecSupportsBackwardScan(Plan *node) |
512 | 0 | { |
513 | 0 | if (node == NULL) |
514 | 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 | | */ |
521 | 0 | if (node->parallel_aware) |
522 | 0 | return false; |
523 | | |
524 | 0 | switch (nodeTag(node)) |
525 | 0 | { |
526 | 0 | case T_Result: |
527 | 0 | if (outerPlan(node) != NULL) |
528 | 0 | return ExecSupportsBackwardScan(outerPlan(node)); |
529 | 0 | else |
530 | 0 | return false; |
531 | | |
532 | 0 | case T_Append: |
533 | 0 | { |
534 | 0 | ListCell *l; |
535 | | |
536 | | /* With async, tuples may be interleaved, so can't back up. */ |
537 | 0 | if (((Append *) node)->nasyncplans > 0) |
538 | 0 | return false; |
539 | | |
540 | 0 | foreach(l, ((Append *) node)->appendplans) |
541 | 0 | { |
542 | 0 | if (!ExecSupportsBackwardScan((Plan *) lfirst(l))) |
543 | 0 | return false; |
544 | 0 | } |
545 | | /* need not check tlist because Append doesn't evaluate it */ |
546 | 0 | return true; |
547 | 0 | } |
548 | | |
549 | 0 | case T_SampleScan: |
550 | | /* Simplify life for tablesample methods by disallowing this */ |
551 | 0 | return false; |
552 | | |
553 | 0 | case T_Gather: |
554 | 0 | return false; |
555 | | |
556 | 0 | case T_IndexScan: |
557 | 0 | return IndexSupportsBackwardScan(((IndexScan *) node)->indexid); |
558 | | |
559 | 0 | case T_IndexOnlyScan: |
560 | 0 | return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid); |
561 | | |
562 | 0 | case T_SubqueryScan: |
563 | 0 | return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan); |
564 | | |
565 | 0 | case T_CustomScan: |
566 | 0 | if (((CustomScan *) node)->flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) |
567 | 0 | return true; |
568 | 0 | return false; |
569 | | |
570 | 0 | case T_SeqScan: |
571 | 0 | case T_TidScan: |
572 | 0 | case T_TidRangeScan: |
573 | 0 | case T_FunctionScan: |
574 | 0 | case T_ValuesScan: |
575 | 0 | case T_CteScan: |
576 | 0 | case T_Material: |
577 | 0 | case T_Sort: |
578 | | /* these don't evaluate tlist */ |
579 | 0 | return true; |
580 | | |
581 | 0 | 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 | 0 | return false; |
588 | | |
589 | 0 | case T_LockRows: |
590 | 0 | case T_Limit: |
591 | 0 | return ExecSupportsBackwardScan(outerPlan(node)); |
592 | | |
593 | 0 | default: |
594 | 0 | return false; |
595 | 0 | } |
596 | 0 | } |
597 | | |
598 | | /* |
599 | | * An IndexScan or IndexOnlyScan node supports backward scan only if the |
600 | | * index's AM does. |
601 | | */ |
602 | | static bool |
603 | | IndexSupportsBackwardScan(Oid indexid) |
604 | 0 | { |
605 | 0 | bool result; |
606 | 0 | HeapTuple ht_idxrel; |
607 | 0 | Form_pg_class idxrelrec; |
608 | 0 | IndexAmRoutine *amroutine; |
609 | | |
610 | | /* Fetch the pg_class tuple of the index relation */ |
611 | 0 | ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid)); |
612 | 0 | if (!HeapTupleIsValid(ht_idxrel)) |
613 | 0 | elog(ERROR, "cache lookup failed for relation %u", indexid); |
614 | 0 | idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); |
615 | | |
616 | | /* Fetch the index AM's API struct */ |
617 | 0 | amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false); |
618 | |
|
619 | 0 | result = amroutine->amcanbackward; |
620 | |
|
621 | 0 | pfree(amroutine); |
622 | 0 | ReleaseSysCache(ht_idxrel); |
623 | |
|
624 | 0 | return result; |
625 | 0 | } |
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 |
636 | | ExecMaterializesOutput(NodeTag plantype) |
637 | 0 | { |
638 | 0 | switch (plantype) |
639 | 0 | { |
640 | 0 | case T_Material: |
641 | 0 | case T_FunctionScan: |
642 | 0 | case T_TableFuncScan: |
643 | 0 | case T_CteScan: |
644 | 0 | case T_NamedTuplestoreScan: |
645 | 0 | case T_WorkTableScan: |
646 | 0 | case T_Sort: |
647 | 0 | return true; |
648 | | |
649 | 0 | default: |
650 | 0 | break; |
651 | 0 | } |
652 | | |
653 | 0 | return false; |
654 | 0 | } |