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