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 : #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 292013 : CteScanNext(CteScanState *node)
33 : {
34 : EState *estate;
35 : ScanDirection dir;
36 : bool forward;
37 : Tuplestorestate *tuplestorestate;
38 : bool eof_tuplestore;
39 : TupleTableSlot *slot;
40 :
41 : /*
42 : * get state info from node
43 : */
44 292013 : estate = node->ss.ps.state;
45 292013 : dir = estate->es_direction;
46 292013 : forward = ScanDirectionIsForward(dir);
47 292013 : tuplestorestate = node->leader->cte_table;
48 292013 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
49 292013 : 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 292013 : eof_tuplestore = tuplestore_ateof(tuplestorestate);
56 :
57 292013 : if (!forward && eof_tuplestore)
58 : {
59 0 : if (!node->leader->eof_cte)
60 : {
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 : }
70 0 : eof_tuplestore = false;
71 : }
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 292013 : if (!eof_tuplestore)
81 : {
82 124677 : if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
83 118597 : return slot;
84 6080 : if (forward)
85 6080 : eof_tuplestore = true;
86 : }
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 173416 : if (eof_tuplestore && !node->leader->eof_cte)
97 : {
98 : 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 169096 : cteslot = ExecProcNode(node->cteplanstate);
105 169084 : if (TupIsNull(cteslot))
106 : {
107 1651 : node->leader->eof_cte = true;
108 1651 : return NULL;
109 : }
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 167433 : 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 167433 : 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 167433 : return ExecCopySlot(slot, cteslot);
134 : }
135 :
136 : /*
137 : * Nothing left ...
138 : */
139 4320 : return ExecClearTuple(slot);
140 : }
141 :
142 : /*
143 : * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
144 : */
145 : static bool
146 2 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
147 : {
148 : /* nothing to check */
149 2 : return true;
150 : }
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 250567 : ExecCteScan(PlanState *pstate)
162 : {
163 250567 : CteScanState *node = castNode(CteScanState, pstate);
164 :
165 250567 : return ExecScan(&node->ss,
166 : (ExecScanAccessMtd) CteScanNext,
167 : (ExecScanRecheckMtd) CteScanRecheck);
168 : }
169 :
170 :
171 : /* ----------------------------------------------------------------
172 : * ExecInitCteScan
173 : * ----------------------------------------------------------------
174 : */
175 : CteScanState *
176 2676 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
177 : {
178 : CteScanState *scanstate;
179 : ParamExecData *prmdata;
180 :
181 : /* check for unsupported flags */
182 : 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 2676 : eflags |= EXEC_FLAG_REWIND;
197 :
198 : /*
199 : * CteScan should not have any children.
200 : */
201 : Assert(outerPlan(node) == NULL);
202 : Assert(innerPlan(node) == NULL);
203 :
204 : /*
205 : * create new CteScanState for node
206 : */
207 2676 : scanstate = makeNode(CteScanState);
208 2676 : scanstate->ss.ps.plan = (Plan *) node;
209 2676 : scanstate->ss.ps.state = estate;
210 2676 : scanstate->ss.ps.ExecProcNode = ExecCteScan;
211 2676 : scanstate->eflags = eflags;
212 2676 : scanstate->cte_table = NULL;
213 2676 : scanstate->eof_cte = false;
214 :
215 : /*
216 : * Find the already-initialized plan for the CTE query.
217 : */
218 5352 : scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
219 2676 : 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 2676 : prmdata = &(estate->es_param_exec_vals[node->cteParam]);
228 : Assert(prmdata->execPlan == NULL);
229 : Assert(!prmdata->isnull);
230 2676 : scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
231 2676 : if (scanstate->leader == NULL)
232 : {
233 : /* I am the leader */
234 1617 : prmdata->value = PointerGetDatum(scanstate);
235 1617 : scanstate->leader = scanstate;
236 1617 : scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
237 1617 : tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
238 1617 : scanstate->readptr = 0;
239 : }
240 : else
241 : {
242 : /* Not the leader */
243 : /* Create my own read pointer, and ensure it is at start */
244 1059 : scanstate->readptr =
245 1059 : tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
246 : scanstate->eflags);
247 1059 : tuplestore_select_read_pointer(scanstate->leader->cte_table,
248 : scanstate->readptr);
249 1059 : tuplestore_rescan(scanstate->leader->cte_table);
250 : }
251 :
252 : /*
253 : * Miscellaneous initialization
254 : *
255 : * create expression context for node
256 : */
257 2676 : 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 2676 : ExecInitScanTupleSlot(estate, &scanstate->ss,
264 : ExecGetResultType(scanstate->cteplanstate),
265 : &TTSOpsMinimalTuple, 0);
266 :
267 : /*
268 : * Initialize result type and projection.
269 : */
270 2676 : ExecInitResultTypeTL(&scanstate->ss.ps);
271 2676 : ExecAssignScanProjectionInfo(&scanstate->ss);
272 :
273 : /*
274 : * initialize child expressions
275 : */
276 2676 : scanstate->ss.ps.qual =
277 2676 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
278 :
279 2676 : return scanstate;
280 : }
281 :
282 : /* ----------------------------------------------------------------
283 : * ExecEndCteScan
284 : *
285 : * frees any storage allocated through C routines.
286 : * ----------------------------------------------------------------
287 : */
288 : void
289 2660 : ExecEndCteScan(CteScanState *node)
290 : {
291 : /*
292 : * If I am the leader, free the tuplestore.
293 : */
294 2660 : if (node->leader == node)
295 : {
296 1602 : tuplestore_end(node->cte_table);
297 1602 : node->cte_table = NULL;
298 : }
299 2660 : }
300 :
301 : /* ----------------------------------------------------------------
302 : * ExecReScanCteScan
303 : *
304 : * Rescans the relation.
305 : * ----------------------------------------------------------------
306 : */
307 : void
308 3762 : ExecReScanCteScan(CteScanState *node)
309 : {
310 3762 : Tuplestorestate *tuplestorestate = node->leader->cte_table;
311 :
312 3762 : if (node->ss.ps.ps_ResultTupleSlot)
313 121 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
314 :
315 3762 : 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 3762 : if (node->leader->cteplanstate->chgParam != NULL)
326 : {
327 304 : tuplestore_clear(tuplestorestate);
328 304 : node->leader->eof_cte = false;
329 : }
330 : else
331 : {
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 3458 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
338 3458 : tuplestore_rescan(tuplestorestate);
339 : }
340 3762 : }
|