/src/postgres/src/backend/executor/nodeCtescan.c
Line | Count | Source |
1 | | /*------------------------------------------------------------------------- |
2 | | * |
3 | | * nodeCtescan.c |
4 | | * routines to handle CteScan nodes. |
5 | | * |
6 | | * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group |
7 | | * Portions Copyright (c) 1994, Regents of the University of California |
8 | | * |
9 | | * |
10 | | * IDENTIFICATION |
11 | | * src/backend/executor/nodeCtescan.c |
12 | | * |
13 | | *------------------------------------------------------------------------- |
14 | | */ |
15 | | |
16 | | #include "postgres.h" |
17 | | |
18 | | #include "executor/executor.h" |
19 | | #include "executor/nodeCtescan.h" |
20 | | #include "miscadmin.h" |
21 | | #include "utils/tuplestore.h" |
22 | | |
23 | | static TupleTableSlot *CteScanNext(CteScanState *node); |
24 | | |
25 | | /* ---------------------------------------------------------------- |
26 | | * CteScanNext |
27 | | * |
28 | | * This is a workhorse for ExecCteScan |
29 | | * ---------------------------------------------------------------- |
30 | | */ |
31 | | static TupleTableSlot * |
32 | | CteScanNext(CteScanState *node) |
33 | 0 | { |
34 | 0 | EState *estate; |
35 | 0 | ScanDirection dir; |
36 | 0 | bool forward; |
37 | 0 | Tuplestorestate *tuplestorestate; |
38 | 0 | bool eof_tuplestore; |
39 | 0 | TupleTableSlot *slot; |
40 | | |
41 | | /* |
42 | | * get state info from node |
43 | | */ |
44 | 0 | estate = node->ss.ps.state; |
45 | 0 | dir = estate->es_direction; |
46 | 0 | forward = ScanDirectionIsForward(dir); |
47 | 0 | tuplestorestate = node->leader->cte_table; |
48 | 0 | tuplestore_select_read_pointer(tuplestorestate, node->readptr); |
49 | 0 | slot = node->ss.ss_ScanTupleSlot; |
50 | | |
51 | | /* |
52 | | * If we are not at the end of the tuplestore, or are going backwards, try |
53 | | * to fetch a tuple from tuplestore. |
54 | | */ |
55 | 0 | eof_tuplestore = tuplestore_ateof(tuplestorestate); |
56 | |
|
57 | 0 | if (!forward && eof_tuplestore) |
58 | 0 | { |
59 | 0 | if (!node->leader->eof_cte) |
60 | 0 | { |
61 | | /* |
62 | | * When reversing direction at tuplestore EOF, the first |
63 | | * gettupleslot call will fetch the last-added tuple; but we want |
64 | | * to return the one before that, if possible. So do an extra |
65 | | * fetch. |
66 | | */ |
67 | 0 | if (!tuplestore_advance(tuplestorestate, forward)) |
68 | 0 | return NULL; /* the tuplestore must be empty */ |
69 | 0 | } |
70 | 0 | eof_tuplestore = false; |
71 | 0 | } |
72 | | |
73 | | /* |
74 | | * If we can fetch another tuple from the tuplestore, return it. |
75 | | * |
76 | | * Note: we have to use copy=true in the tuplestore_gettupleslot call, |
77 | | * because we are sharing the tuplestore with other nodes that might write |
78 | | * into the tuplestore before we get called again. |
79 | | */ |
80 | 0 | if (!eof_tuplestore) |
81 | 0 | { |
82 | 0 | if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot)) |
83 | 0 | return slot; |
84 | 0 | if (forward) |
85 | 0 | eof_tuplestore = true; |
86 | 0 | } |
87 | | |
88 | | /* |
89 | | * If necessary, try to fetch another row from the CTE query. |
90 | | * |
91 | | * Note: the eof_cte state variable exists to short-circuit further calls |
92 | | * of the CTE plan. It's not optional, unfortunately, because some plan |
93 | | * node types are not robust about being called again when they've already |
94 | | * returned NULL. |
95 | | */ |
96 | 0 | if (eof_tuplestore && !node->leader->eof_cte) |
97 | 0 | { |
98 | 0 | TupleTableSlot *cteslot; |
99 | | |
100 | | /* |
101 | | * We can only get here with forward==true, so no need to worry about |
102 | | * which direction the subplan will go. |
103 | | */ |
104 | 0 | cteslot = ExecProcNode(node->cteplanstate); |
105 | 0 | if (TupIsNull(cteslot)) |
106 | 0 | { |
107 | 0 | node->leader->eof_cte = true; |
108 | 0 | return NULL; |
109 | 0 | } |
110 | | |
111 | | /* |
112 | | * There are corner cases where the subplan could change which |
113 | | * tuplestore read pointer is active, so be sure to reselect ours |
114 | | * before storing the tuple we got. |
115 | | */ |
116 | 0 | tuplestore_select_read_pointer(tuplestorestate, node->readptr); |
117 | | |
118 | | /* |
119 | | * Append a copy of the returned tuple to tuplestore. NOTE: because |
120 | | * our read pointer is certainly in EOF state, its read position will |
121 | | * move forward over the added tuple. This is what we want. Also, |
122 | | * any other readers will *not* move past the new tuple, which is what |
123 | | * they want. |
124 | | */ |
125 | 0 | tuplestore_puttupleslot(tuplestorestate, cteslot); |
126 | | |
127 | | /* |
128 | | * We MUST copy the CTE query's output tuple into our own slot. This |
129 | | * is because other CteScan nodes might advance the CTE query before |
130 | | * we are called again, and our output tuple must stay stable over |
131 | | * that. |
132 | | */ |
133 | 0 | return ExecCopySlot(slot, cteslot); |
134 | 0 | } |
135 | | |
136 | | /* |
137 | | * Nothing left ... |
138 | | */ |
139 | 0 | return ExecClearTuple(slot); |
140 | 0 | } |
141 | | |
142 | | /* |
143 | | * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual |
144 | | */ |
145 | | static bool |
146 | | CteScanRecheck(CteScanState *node, TupleTableSlot *slot) |
147 | 0 | { |
148 | | /* nothing to check */ |
149 | 0 | return true; |
150 | 0 | } |
151 | | |
152 | | /* ---------------------------------------------------------------- |
153 | | * ExecCteScan(node) |
154 | | * |
155 | | * Scans the CTE sequentially and returns the next qualifying tuple. |
156 | | * We call the ExecScan() routine and pass it the appropriate |
157 | | * access method functions. |
158 | | * ---------------------------------------------------------------- |
159 | | */ |
160 | | static TupleTableSlot * |
161 | | ExecCteScan(PlanState *pstate) |
162 | 0 | { |
163 | 0 | CteScanState *node = castNode(CteScanState, pstate); |
164 | |
|
165 | 0 | return ExecScan(&node->ss, |
166 | 0 | (ExecScanAccessMtd) CteScanNext, |
167 | 0 | (ExecScanRecheckMtd) CteScanRecheck); |
168 | 0 | } |
169 | | |
170 | | |
171 | | /* ---------------------------------------------------------------- |
172 | | * ExecInitCteScan |
173 | | * ---------------------------------------------------------------- |
174 | | */ |
175 | | CteScanState * |
176 | | ExecInitCteScan(CteScan *node, EState *estate, int eflags) |
177 | 0 | { |
178 | 0 | CteScanState *scanstate; |
179 | 0 | ParamExecData *prmdata; |
180 | | |
181 | | /* check for unsupported flags */ |
182 | 0 | Assert(!(eflags & EXEC_FLAG_MARK)); |
183 | | |
184 | | /* |
185 | | * For the moment we have to force the tuplestore to allow REWIND, because |
186 | | * we might be asked to rescan the CTE even though upper levels didn't |
187 | | * tell us to be prepared to do it efficiently. Annoying, since this |
188 | | * prevents truncation of the tuplestore. XXX FIXME |
189 | | * |
190 | | * Note: if we are in an EPQ recheck plan tree, it's likely that no access |
191 | | * to the tuplestore is needed at all, making this even more annoying. |
192 | | * It's not worth improving that as long as all the read pointers would |
193 | | * have REWIND anyway, but if we ever improve this logic then that aspect |
194 | | * should be considered too. |
195 | | */ |
196 | 0 | eflags |= EXEC_FLAG_REWIND; |
197 | | |
198 | | /* |
199 | | * CteScan should not have any children. |
200 | | */ |
201 | 0 | Assert(outerPlan(node) == NULL); |
202 | 0 | Assert(innerPlan(node) == NULL); |
203 | | |
204 | | /* |
205 | | * create new CteScanState for node |
206 | | */ |
207 | 0 | scanstate = makeNode(CteScanState); |
208 | 0 | scanstate->ss.ps.plan = (Plan *) node; |
209 | 0 | scanstate->ss.ps.state = estate; |
210 | 0 | scanstate->ss.ps.ExecProcNode = ExecCteScan; |
211 | 0 | scanstate->eflags = eflags; |
212 | 0 | scanstate->cte_table = NULL; |
213 | 0 | scanstate->eof_cte = false; |
214 | | |
215 | | /* |
216 | | * Find the already-initialized plan for the CTE query. |
217 | | */ |
218 | 0 | scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates, |
219 | 0 | node->ctePlanId - 1); |
220 | | |
221 | | /* |
222 | | * The Param slot associated with the CTE query is used to hold a pointer |
223 | | * to the CteState of the first CteScan node that initializes for this |
224 | | * CTE. This node will be the one that holds the shared state for all the |
225 | | * CTEs, particularly the shared tuplestore. |
226 | | */ |
227 | 0 | prmdata = &(estate->es_param_exec_vals[node->cteParam]); |
228 | 0 | Assert(prmdata->execPlan == NULL); |
229 | 0 | Assert(!prmdata->isnull); |
230 | 0 | scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value)); |
231 | 0 | if (scanstate->leader == NULL) |
232 | 0 | { |
233 | | /* I am the leader */ |
234 | 0 | prmdata->value = PointerGetDatum(scanstate); |
235 | 0 | scanstate->leader = scanstate; |
236 | 0 | scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem); |
237 | 0 | tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags); |
238 | 0 | scanstate->readptr = 0; |
239 | 0 | } |
240 | 0 | else |
241 | 0 | { |
242 | | /* Not the leader */ |
243 | | /* Create my own read pointer, and ensure it is at start */ |
244 | 0 | scanstate->readptr = |
245 | 0 | tuplestore_alloc_read_pointer(scanstate->leader->cte_table, |
246 | 0 | scanstate->eflags); |
247 | 0 | tuplestore_select_read_pointer(scanstate->leader->cte_table, |
248 | 0 | scanstate->readptr); |
249 | 0 | tuplestore_rescan(scanstate->leader->cte_table); |
250 | 0 | } |
251 | | |
252 | | /* |
253 | | * Miscellaneous initialization |
254 | | * |
255 | | * create expression context for node |
256 | | */ |
257 | 0 | ExecAssignExprContext(estate, &scanstate->ss.ps); |
258 | | |
259 | | /* |
260 | | * The scan tuple type (ie, the rowtype we expect to find in the work |
261 | | * table) is the same as the result rowtype of the CTE query. |
262 | | */ |
263 | 0 | ExecInitScanTupleSlot(estate, &scanstate->ss, |
264 | 0 | ExecGetResultType(scanstate->cteplanstate), |
265 | 0 | &TTSOpsMinimalTuple, 0); |
266 | | |
267 | | /* |
268 | | * Initialize result type and projection. |
269 | | */ |
270 | 0 | ExecInitResultTypeTL(&scanstate->ss.ps); |
271 | 0 | ExecAssignScanProjectionInfo(&scanstate->ss); |
272 | | |
273 | | /* |
274 | | * initialize child expressions |
275 | | */ |
276 | 0 | scanstate->ss.ps.qual = |
277 | 0 | ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate); |
278 | |
|
279 | 0 | return scanstate; |
280 | 0 | } |
281 | | |
282 | | /* ---------------------------------------------------------------- |
283 | | * ExecEndCteScan |
284 | | * |
285 | | * frees any storage allocated through C routines. |
286 | | * ---------------------------------------------------------------- |
287 | | */ |
288 | | void |
289 | | ExecEndCteScan(CteScanState *node) |
290 | 0 | { |
291 | | /* |
292 | | * If I am the leader, free the tuplestore. |
293 | | */ |
294 | 0 | if (node->leader == node) |
295 | 0 | { |
296 | 0 | tuplestore_end(node->cte_table); |
297 | 0 | node->cte_table = NULL; |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | /* ---------------------------------------------------------------- |
302 | | * ExecReScanCteScan |
303 | | * |
304 | | * Rescans the relation. |
305 | | * ---------------------------------------------------------------- |
306 | | */ |
307 | | void |
308 | | ExecReScanCteScan(CteScanState *node) |
309 | 0 | { |
310 | 0 | Tuplestorestate *tuplestorestate = node->leader->cte_table; |
311 | |
|
312 | 0 | if (node->ss.ps.ps_ResultTupleSlot) |
313 | 0 | ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); |
314 | |
|
315 | 0 | ExecScanReScan(&node->ss); |
316 | | |
317 | | /* |
318 | | * Clear the tuplestore if a new scan of the underlying CTE is required. |
319 | | * This implicitly resets all the tuplestore's read pointers. Note that |
320 | | * multiple CTE nodes might redundantly clear the tuplestore; that's OK, |
321 | | * and not unduly expensive. We'll stop taking this path as soon as |
322 | | * somebody has attempted to read something from the underlying CTE |
323 | | * (thereby causing its chgParam to be cleared). |
324 | | */ |
325 | 0 | if (node->leader->cteplanstate->chgParam != NULL) |
326 | 0 | { |
327 | 0 | tuplestore_clear(tuplestorestate); |
328 | 0 | node->leader->eof_cte = false; |
329 | 0 | } |
330 | 0 | else |
331 | 0 | { |
332 | | /* |
333 | | * Else, just rewind my own pointer. Either the underlying CTE |
334 | | * doesn't need a rescan (and we can re-read what's in the tuplestore |
335 | | * now), or somebody else already took care of it. |
336 | | */ |
337 | 0 | tuplestore_select_read_pointer(tuplestorestate, node->readptr); |
338 | 0 | tuplestore_rescan(tuplestorestate); |
339 | 0 | } |
340 | 0 | } |