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