/Users/deen/code/yugabyte-db/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-2018, 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 "executor/execdebug.h" |
18 | | #include "executor/nodeAgg.h" |
19 | | #include "executor/nodeAppend.h" |
20 | | #include "executor/nodeBitmapAnd.h" |
21 | | #include "executor/nodeBitmapHeapscan.h" |
22 | | #include "executor/nodeBitmapIndexscan.h" |
23 | | #include "executor/nodeBitmapOr.h" |
24 | | #include "executor/nodeCtescan.h" |
25 | | #include "executor/nodeCustom.h" |
26 | | #include "executor/nodeForeignscan.h" |
27 | | #include "executor/nodeFunctionscan.h" |
28 | | #include "executor/nodeGather.h" |
29 | | #include "executor/nodeGatherMerge.h" |
30 | | #include "executor/nodeGroup.h" |
31 | | #include "executor/nodeGroup.h" |
32 | | #include "executor/nodeHash.h" |
33 | | #include "executor/nodeHashjoin.h" |
34 | | #include "executor/nodeIndexonlyscan.h" |
35 | | #include "executor/nodeIndexscan.h" |
36 | | #include "executor/nodeLimit.h" |
37 | | #include "executor/nodeLockRows.h" |
38 | | #include "executor/nodeMaterial.h" |
39 | | #include "executor/nodeMergeAppend.h" |
40 | | #include "executor/nodeMergejoin.h" |
41 | | #include "executor/nodeModifyTable.h" |
42 | | #include "executor/nodeNamedtuplestorescan.h" |
43 | | #include "executor/nodeNestloop.h" |
44 | | #include "executor/nodeProjectSet.h" |
45 | | #include "executor/nodeRecursiveunion.h" |
46 | | #include "executor/nodeResult.h" |
47 | | #include "executor/nodeSamplescan.h" |
48 | | #include "executor/nodeSeqscan.h" |
49 | | #include "executor/nodeSetOp.h" |
50 | | #include "executor/nodeSort.h" |
51 | | #include "executor/nodeSubplan.h" |
52 | | #include "executor/nodeSubqueryscan.h" |
53 | | #include "executor/nodeTableFuncscan.h" |
54 | | #include "executor/nodeTidscan.h" |
55 | | #include "executor/nodeUnique.h" |
56 | | #include "executor/nodeValuesscan.h" |
57 | | #include "executor/nodeWindowAgg.h" |
58 | | #include "executor/nodeWorktablescan.h" |
59 | | #include "nodes/nodeFuncs.h" |
60 | | #include "nodes/relation.h" |
61 | | #include "utils/rel.h" |
62 | | #include "utils/syscache.h" |
63 | | |
64 | | |
65 | | static bool IndexSupportsBackwardScan(Oid indexid); |
66 | | |
67 | | |
68 | | /* |
69 | | * ExecReScan |
70 | | * Reset a plan node so that its output can be re-scanned. |
71 | | * |
72 | | * Note that if the plan node has parameters that have changed value, |
73 | | * the output might be different from last time. |
74 | | */ |
75 | | void |
76 | | ExecReScan(PlanState *node) |
77 | 143k | { |
78 | | /* If collecting timing stats, update them */ |
79 | 143k | if (node->instrument) |
80 | 0 | InstrEndLoop(node->instrument); |
81 | | |
82 | | /* |
83 | | * If we have changed parameters, propagate that info. |
84 | | * |
85 | | * Note: ExecReScanSetParamPlan() can add bits to node->chgParam, |
86 | | * corresponding to the output param(s) that the InitPlan will update. |
87 | | * Since we make only one pass over the list, that means that an InitPlan |
88 | | * can depend on the output param(s) of a sibling InitPlan only if that |
89 | | * sibling appears earlier in the list. This is workable for now given |
90 | | * the limited ways in which one InitPlan could depend on another, but |
91 | | * eventually we might need to work harder (or else make the planner |
92 | | * enlarge the extParam/allParam sets to include the params of depended-on |
93 | | * InitPlans). |
94 | | */ |
95 | 143k | if (node->chgParam != NULL) |
96 | 141k | { |
97 | 141k | ListCell *l; |
98 | | |
99 | 141k | foreach(l, node->initPlan) |
100 | 0 | { |
101 | 0 | SubPlanState *sstate = (SubPlanState *) lfirst(l); |
102 | 0 | PlanState *splan = sstate->planstate; |
103 | |
|
104 | 0 | if (splan->plan->extParam != NULL) /* don't care about child |
105 | | * local Params */ |
106 | 0 | UpdateChangedParamSet(splan, node->chgParam); |
107 | 0 | if (splan->chgParam != NULL) |
108 | 0 | ExecReScanSetParamPlan(sstate, node); |
109 | 0 | } |
110 | 141k | foreach(l, node->subPlan) |
111 | 14 | { |
112 | 14 | SubPlanState *sstate = (SubPlanState *) lfirst(l); |
113 | 14 | PlanState *splan = sstate->planstate; |
114 | | |
115 | 14 | if (splan->plan->extParam != NULL) |
116 | 11 | UpdateChangedParamSet(splan, node->chgParam); |
117 | 14 | } |
118 | | /* Well. Now set chgParam for left/right trees. */ |
119 | 141k | if (node->lefttree != NULL) |
120 | 9.54k | UpdateChangedParamSet(node->lefttree, node->chgParam); |
121 | 141k | if (node->righttree != NULL) |
122 | 8.31k | UpdateChangedParamSet(node->righttree, node->chgParam); |
123 | 141k | } |
124 | | |
125 | | /* Call expression callbacks */ |
126 | 143k | if (node->ps_ExprContext) |
127 | 141k | ReScanExprContext(node->ps_ExprContext); |
128 | | |
129 | | /* And do node-type-specific processing */ |
130 | 143k | switch (nodeTag(node)) |
131 | 143k | { |
132 | 67 | case T_ResultState: |
133 | 67 | ExecReScanResult((ResultState *) node); |
134 | 67 | break; |
135 | | |
136 | 0 | case T_ProjectSetState: |
137 | 0 | ExecReScanProjectSet((ProjectSetState *) node); |
138 | 0 | break; |
139 | | |
140 | 0 | case T_ModifyTableState: |
141 | 0 | ExecReScanModifyTable((ModifyTableState *) node); |
142 | 0 | break; |
143 | | |
144 | 0 | case T_AppendState: |
145 | 0 | ExecReScanAppend((AppendState *) node); |
146 | 0 | break; |
147 | | |
148 | 0 | case T_MergeAppendState: |
149 | 0 | ExecReScanMergeAppend((MergeAppendState *) node); |
150 | 0 | break; |
151 | | |
152 | 0 | case T_RecursiveUnionState: |
153 | 0 | ExecReScanRecursiveUnion((RecursiveUnionState *) node); |
154 | 0 | break; |
155 | | |
156 | 0 | case T_BitmapAndState: |
157 | 0 | ExecReScanBitmapAnd((BitmapAndState *) node); |
158 | 0 | break; |
159 | | |
160 | 0 | case T_BitmapOrState: |
161 | 0 | ExecReScanBitmapOr((BitmapOrState *) node); |
162 | 0 | break; |
163 | | |
164 | 0 | case T_SeqScanState: |
165 | 0 | ExecReScanSeqScan((SeqScanState *) node); |
166 | 0 | break; |
167 | | |
168 | 0 | case T_SampleScanState: |
169 | 0 | ExecReScanSampleScan((SampleScanState *) node); |
170 | 0 | break; |
171 | | |
172 | 0 | case T_GatherState: |
173 | 0 | ExecReScanGather((GatherState *) node); |
174 | 0 | break; |
175 | | |
176 | 0 | case T_GatherMergeState: |
177 | 0 | ExecReScanGatherMerge((GatherMergeState *) node); |
178 | 0 | break; |
179 | | |
180 | 129k | case T_IndexScanState: |
181 | 129k | ExecReScanIndexScan((IndexScanState *) node); |
182 | 129k | break; |
183 | | |
184 | 1 | case T_IndexOnlyScanState: |
185 | 1 | ExecReScanIndexOnlyScan((IndexOnlyScanState *) node); |
186 | 1 | break; |
187 | | |
188 | 0 | case T_BitmapIndexScanState: |
189 | 0 | ExecReScanBitmapIndexScan((BitmapIndexScanState *) node); |
190 | 0 | break; |
191 | | |
192 | 0 | case T_BitmapHeapScanState: |
193 | 0 | ExecReScanBitmapHeapScan((BitmapHeapScanState *) node); |
194 | 0 | break; |
195 | | |
196 | 0 | case T_TidScanState: |
197 | 0 | ExecReScanTidScan((TidScanState *) node); |
198 | 0 | break; |
199 | | |
200 | 129 | case T_SubqueryScanState: |
201 | 129 | ExecReScanSubqueryScan((SubqueryScanState *) node); |
202 | 129 | break; |
203 | | |
204 | 298 | case T_FunctionScanState: |
205 | 298 | ExecReScanFunctionScan((FunctionScanState *) node); |
206 | 298 | break; |
207 | | |
208 | 0 | case T_TableFuncScanState: |
209 | 0 | ExecReScanTableFuncScan((TableFuncScanState *) node); |
210 | 0 | break; |
211 | | |
212 | 16 | case T_ValuesScanState: |
213 | 16 | ExecReScanValuesScan((ValuesScanState *) node); |
214 | 16 | break; |
215 | | |
216 | 0 | case T_CteScanState: |
217 | 0 | ExecReScanCteScan((CteScanState *) node); |
218 | 0 | break; |
219 | | |
220 | 0 | case T_NamedTuplestoreScanState: |
221 | 0 | ExecReScanNamedTuplestoreScan((NamedTuplestoreScanState *) node); |
222 | 0 | break; |
223 | | |
224 | 0 | case T_WorkTableScanState: |
225 | 0 | ExecReScanWorkTableScan((WorkTableScanState *) node); |
226 | 0 | break; |
227 | | |
228 | 2.04k | case T_ForeignScanState: |
229 | 2.04k | ExecReScanForeignScan((ForeignScanState *) node); |
230 | 2.04k | break; |
231 | | |
232 | 0 | case T_CustomScanState: |
233 | 0 | ExecReScanCustomScan((CustomScanState *) node); |
234 | 0 | break; |
235 | | |
236 | 8.28k | case T_NestLoopState: |
237 | 8.28k | ExecReScanNestLoop((NestLoopState *) node); |
238 | 8.28k | break; |
239 | | |
240 | 0 | case T_MergeJoinState: |
241 | 0 | ExecReScanMergeJoin((MergeJoinState *) node); |
242 | 0 | break; |
243 | | |
244 | 69 | case T_HashJoinState: |
245 | 69 | ExecReScanHashJoin((HashJoinState *) node); |
246 | 69 | break; |
247 | | |
248 | 1.37k | case T_MaterialState: |
249 | 1.37k | ExecReScanMaterial((MaterialState *) node); |
250 | 1.37k | break; |
251 | | |
252 | 205 | case T_SortState: |
253 | 205 | ExecReScanSort((SortState *) node); |
254 | 205 | break; |
255 | | |
256 | 0 | case T_GroupState: |
257 | 0 | ExecReScanGroup((GroupState *) node); |
258 | 0 | break; |
259 | | |
260 | 1.04k | case T_AggState: |
261 | 1.04k | ExecReScanAgg((AggState *) node); |
262 | 1.04k | break; |
263 | | |
264 | 0 | case T_WindowAggState: |
265 | 0 | ExecReScanWindowAgg((WindowAggState *) node); |
266 | 0 | break; |
267 | | |
268 | 0 | case T_UniqueState: |
269 | 0 | ExecReScanUnique((UniqueState *) node); |
270 | 0 | break; |
271 | | |
272 | 24 | case T_HashState: |
273 | 24 | ExecReScanHash((HashState *) node); |
274 | 24 | break; |
275 | | |
276 | 0 | case T_SetOpState: |
277 | 0 | ExecReScanSetOp((SetOpState *) node); |
278 | 0 | break; |
279 | | |
280 | 0 | case T_LockRowsState: |
281 | 0 | ExecReScanLockRows((LockRowsState *) node); |
282 | 0 | break; |
283 | | |
284 | 103 | case T_LimitState: |
285 | 103 | ExecReScanLimit((LimitState *) node); |
286 | 103 | break; |
287 | | |
288 | 0 | default: |
289 | 0 | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); |
290 | 0 | break; |
291 | 143k | } |
292 | | |
293 | 143k | if (node->chgParam != NULL) |
294 | 141k | { |
295 | 141k | bms_free(node->chgParam); |
296 | 141k | node->chgParam = NULL; |
297 | 141k | } |
298 | 143k | } |
299 | | |
300 | | /* |
301 | | * ExecMarkPos |
302 | | * |
303 | | * Marks the current scan position. |
304 | | * |
305 | | * NOTE: mark/restore capability is currently needed only for plan nodes |
306 | | * that are the immediate inner child of a MergeJoin node. Since MergeJoin |
307 | | * requires sorted input, there is never any need to support mark/restore in |
308 | | * node types that cannot produce sorted output. There are some cases in |
309 | | * which a node can pass through sorted data from its child; if we don't |
310 | | * implement mark/restore for such a node type, the planner compensates by |
311 | | * inserting a Material node above that node. |
312 | | */ |
313 | | void |
314 | | ExecMarkPos(PlanState *node) |
315 | 56 | { |
316 | 56 | switch (nodeTag(node)) |
317 | 56 | { |
318 | 0 | case T_IndexScanState: |
319 | 0 | ExecIndexMarkPos((IndexScanState *) node); |
320 | 0 | break; |
321 | | |
322 | 0 | case T_IndexOnlyScanState: |
323 | 0 | ExecIndexOnlyMarkPos((IndexOnlyScanState *) node); |
324 | 0 | break; |
325 | | |
326 | 0 | case T_CustomScanState: |
327 | 0 | ExecCustomMarkPos((CustomScanState *) node); |
328 | 0 | break; |
329 | | |
330 | 35 | case T_MaterialState: |
331 | 35 | ExecMaterialMarkPos((MaterialState *) node); |
332 | 35 | break; |
333 | | |
334 | 21 | case T_SortState: |
335 | 21 | ExecSortMarkPos((SortState *) node); |
336 | 21 | break; |
337 | | |
338 | 0 | case T_ResultState: |
339 | 0 | ExecResultMarkPos((ResultState *) node); |
340 | 0 | break; |
341 | | |
342 | 0 | default: |
343 | | /* don't make hard error unless caller asks to restore... */ |
344 | 0 | elog(DEBUG2, "unrecognized node type: %d", (int) nodeTag(node)); |
345 | 0 | break; |
346 | 56 | } |
347 | 56 | } |
348 | | |
349 | | /* |
350 | | * ExecRestrPos |
351 | | * |
352 | | * restores the scan position previously saved with ExecMarkPos() |
353 | | * |
354 | | * NOTE: the semantics of this are that the first ExecProcNode following |
355 | | * the restore operation will yield the same tuple as the first one following |
356 | | * the mark operation. It is unspecified what happens to the plan node's |
357 | | * result TupleTableSlot. (In most cases the result slot is unchanged by |
358 | | * a restore, but the node may choose to clear it or to load it with the |
359 | | * restored-to tuple.) Hence the caller should discard any previously |
360 | | * returned TupleTableSlot after doing a restore. |
361 | | */ |
362 | | void |
363 | | ExecRestrPos(PlanState *node) |
364 | 1.00k | { |
365 | 1.00k | switch (nodeTag(node)) |
366 | 1.00k | { |
367 | 0 | case T_IndexScanState: |
368 | 0 | ExecIndexRestrPos((IndexScanState *) node); |
369 | 0 | break; |
370 | | |
371 | 0 | case T_IndexOnlyScanState: |
372 | 0 | ExecIndexOnlyRestrPos((IndexOnlyScanState *) node); |
373 | 0 | break; |
374 | | |
375 | 0 | case T_CustomScanState: |
376 | 0 | ExecCustomRestrPos((CustomScanState *) node); |
377 | 0 | break; |
378 | | |
379 | 5 | case T_MaterialState: |
380 | 5 | ExecMaterialRestrPos((MaterialState *) node); |
381 | 5 | break; |
382 | | |
383 | 1.00k | case T_SortState: |
384 | 1.00k | ExecSortRestrPos((SortState *) node); |
385 | 1.00k | break; |
386 | | |
387 | 0 | case T_ResultState: |
388 | 0 | ExecResultRestrPos((ResultState *) node); |
389 | 0 | break; |
390 | | |
391 | 0 | default: |
392 | 0 | elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); |
393 | 0 | break; |
394 | 1.00k | } |
395 | 1.00k | } |
396 | | |
397 | | /* |
398 | | * ExecSupportsMarkRestore - does a Path support mark/restore? |
399 | | * |
400 | | * This is used during planning and so must accept a Path, not a Plan. |
401 | | * We keep it here to be adjacent to the routines above, which also must |
402 | | * know which plan types support mark/restore. |
403 | | */ |
404 | | bool |
405 | | ExecSupportsMarkRestore(Path *pathnode) |
406 | 227 | { |
407 | | /* |
408 | | * For consistency with the routines above, we do not examine the nodeTag |
409 | | * but rather the pathtype, which is the Plan node type the Path would |
410 | | * produce. |
411 | | */ |
412 | 227 | switch (pathnode->pathtype) |
413 | 227 | { |
414 | 139 | case T_IndexScan: |
415 | 139 | case T_IndexOnlyScan: |
416 | | |
417 | | /* |
418 | | * Yugabyte index scan do not support mark/restore, that would force |
419 | | * a Materialize plan node on top of the scan. |
420 | | * TODO Consider to support mark/restore. Though Materialize remote |
421 | | * index scan may be more efficient solution anyway. |
422 | | */ |
423 | 139 | return false; |
424 | | |
425 | 0 | case T_Material: |
426 | 0 | case T_Sort: |
427 | 0 | return true; |
428 | |
|
429 | 0 | case T_CustomScan: |
430 | 0 | { |
431 | 0 | CustomPath *customPath = castNode(CustomPath, pathnode); |
432 | |
|
433 | 0 | if (customPath->flags & CUSTOMPATH_SUPPORT_MARK_RESTORE) |
434 | 0 | return true; |
435 | 0 | return false; |
436 | 0 | } |
437 | 0 | case T_Result: |
438 | | |
439 | | /* |
440 | | * Result supports mark/restore iff it has a child plan that does. |
441 | | * |
442 | | * We have to be careful here because there is more than one Path |
443 | | * type that can produce a Result plan node. |
444 | | */ |
445 | 0 | if (IsA(pathnode, ProjectionPath)) |
446 | 0 | return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath); |
447 | 0 | else if (IsA(pathnode, MinMaxAggPath)) |
448 | 0 | return false; /* childless Result */ |
449 | 0 | else |
450 | 0 | { |
451 | 0 | Assert(IsA(pathnode, ResultPath)); |
452 | 0 | return false; /* childless Result */ |
453 | 0 | } |
454 | | |
455 | 88 | default: |
456 | 88 | break; |
457 | 88 | } |
458 | | |
459 | 88 | return false; |
460 | 88 | } |
461 | | |
462 | | /* |
463 | | * ExecSupportsBackwardScan - does a plan type support backwards scanning? |
464 | | * |
465 | | * Ideally, all plan types would support backwards scan, but that seems |
466 | | * unlikely to happen soon. In some cases, a plan node passes the backwards |
467 | | * scan down to its children, and so supports backwards scan only if its |
468 | | * children do. Therefore, this routine must be passed a complete plan tree. |
469 | | */ |
470 | | bool |
471 | | ExecSupportsBackwardScan(Plan *node) |
472 | 1.45k | { |
473 | 1.45k | if (node == NULL) |
474 | 0 | return false; |
475 | | |
476 | | /* |
477 | | * Parallel-aware nodes return a subset of the tuples in each worker, and |
478 | | * in general we can't expect to have enough bookkeeping state to know |
479 | | * which ones we returned in this worker as opposed to some other worker. |
480 | | */ |
481 | 1.45k | if (node->parallel_aware) |
482 | 0 | return false; |
483 | | |
484 | 1.45k | switch (nodeTag(node)) |
485 | 1.45k | { |
486 | 0 | case T_Result: |
487 | 0 | if (outerPlan(node) != NULL) |
488 | 0 | return ExecSupportsBackwardScan(outerPlan(node)); |
489 | 0 | else |
490 | 0 | return false; |
491 | | |
492 | 0 | case T_Append: |
493 | 0 | { |
494 | 0 | ListCell *l; |
495 | |
|
496 | 0 | foreach(l, ((Append *) node)->appendplans) |
497 | 0 | { |
498 | 0 | if (!ExecSupportsBackwardScan((Plan *) lfirst(l))) |
499 | 0 | return false; |
500 | 0 | } |
501 | | /* need not check tlist because Append doesn't evaluate it */ |
502 | 0 | return true; |
503 | 0 | } |
504 | |
|
505 | 0 | case T_SampleScan: |
506 | | /* Simplify life for tablesample methods by disallowing this */ |
507 | 0 | return false; |
508 | |
|
509 | 0 | case T_Gather: |
510 | 0 | return false; |
511 | |
|
512 | 9 | case T_IndexScan: |
513 | 9 | return IndexSupportsBackwardScan(((IndexScan *) node)->indexid); |
514 | |
|
515 | 0 | case T_IndexOnlyScan: |
516 | 0 | return IndexSupportsBackwardScan(((IndexOnlyScan *) node)->indexid); |
517 | |
|
518 | 0 | case T_SubqueryScan: |
519 | 0 | return ExecSupportsBackwardScan(((SubqueryScan *) node)->subplan); |
520 | |
|
521 | 0 | case T_CustomScan: |
522 | 0 | { |
523 | 0 | uint32 flags = ((CustomScan *) node)->flags; |
524 | |
|
525 | 0 | if (flags & CUSTOMPATH_SUPPORT_BACKWARD_SCAN) |
526 | 0 | return true; |
527 | 0 | } |
528 | 0 | return false; |
529 | |
|
530 | 0 | case T_SeqScan: |
531 | 0 | case T_TidScan: |
532 | 0 | case T_FunctionScan: |
533 | 0 | case T_ValuesScan: |
534 | 0 | case T_CteScan: |
535 | 0 | case T_Material: |
536 | 53 | case T_Sort: |
537 | 53 | return true; |
538 | |
|
539 | 0 | case T_LockRows: |
540 | 0 | case T_Limit: |
541 | 0 | return ExecSupportsBackwardScan(outerPlan(node)); |
542 | |
|
543 | 1.39k | default: |
544 | 1.39k | return false; |
545 | 1.45k | } |
546 | 1.45k | } |
547 | | |
548 | | /* |
549 | | * An IndexScan or IndexOnlyScan node supports backward scan only if the |
550 | | * index's AM does. |
551 | | */ |
552 | | static bool |
553 | | IndexSupportsBackwardScan(Oid indexid) |
554 | 9 | { |
555 | 9 | bool result; |
556 | 9 | HeapTuple ht_idxrel; |
557 | 9 | Form_pg_class idxrelrec; |
558 | 9 | IndexAmRoutine *amroutine; |
559 | | |
560 | | /* Fetch the pg_class tuple of the index relation */ |
561 | 9 | ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexid)); |
562 | 9 | if (!HeapTupleIsValid(ht_idxrel)) |
563 | 0 | elog(ERROR, "cache lookup failed for relation %u", indexid); |
564 | 9 | idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel); |
565 | | |
566 | | /* Fetch the index AM's API struct */ |
567 | 9 | amroutine = GetIndexAmRoutineByAmId(idxrelrec->relam, false); |
568 | | |
569 | 9 | result = amroutine->amcanbackward; |
570 | | |
571 | 9 | pfree(amroutine); |
572 | 9 | ReleaseSysCache(ht_idxrel); |
573 | | |
574 | 9 | return result; |
575 | 9 | } |
576 | | |
577 | | /* |
578 | | * ExecMaterializesOutput - does a plan type materialize its output? |
579 | | * |
580 | | * Returns true if the plan node type is one that automatically materializes |
581 | | * its output (typically by keeping it in a tuplestore). For such plans, |
582 | | * a rescan without any parameter change will have zero startup cost and |
583 | | * very low per-tuple cost. |
584 | | */ |
585 | | bool |
586 | | ExecMaterializesOutput(NodeTag plantype) |
587 | 27.4k | { |
588 | 27.4k | switch (plantype) |
589 | 27.4k | { |
590 | 0 | case T_Material: |
591 | 1.13k | case T_FunctionScan: |
592 | 1.13k | case T_TableFuncScan: |
593 | 7.43k | case T_CteScan: |
594 | 7.43k | case T_NamedTuplestoreScan: |
595 | 7.43k | case T_WorkTableScan: |
596 | 7.43k | case T_Sort: |
597 | 7.43k | return true; |
598 | | |
599 | 20.0k | default: |
600 | 20.0k | break; |
601 | 20.0k | } |
602 | | |
603 | 20.0k | return false; |
604 | 20.0k | } |