Age Owner Branch data TLA 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 *
6422 tgl@sss.pgh.pa.us 32 :CBC 290888 : 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 : 290888 : estate = node->ss.ps.state;
45 : 290888 : dir = estate->es_direction;
46 : 290888 : forward = ScanDirectionIsForward(dir);
47 : 290888 : tuplestorestate = node->leader->cte_table;
48 : 290888 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
49 : 290888 : 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 : 290888 : eof_tuplestore = tuplestore_ateof(tuplestorestate);
56 : :
57 [ - + - - ]: 290888 : if (!forward && eof_tuplestore)
58 : : {
6422 tgl@sss.pgh.pa.us 59 [ # # ]:UBC 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 : : */
6422 tgl@sss.pgh.pa.us 80 [ + + ]:CBC 290888 : if (!eof_tuplestore)
81 : : {
6248 82 [ + + ]: 122643 : if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
6422 83 : 116921 : return slot;
84 [ + - ]: 5722 : if (forward)
85 : 5722 : 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 [ + - + + ]: 173967 : 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 : 170011 : cteslot = ExecProcNode(node->cteplanstate);
105 [ + + + + ]: 169999 : if (TupIsNull(cteslot))
106 : : {
107 : 1657 : node->leader->eof_cte = true;
108 : 1657 : 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 : : */
2997 116 : 168342 : 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 : : */
6422 125 : 168342 : 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 : 168342 : return ExecCopySlot(slot, cteslot);
134 : : }
135 : :
136 : : /*
137 : : * Nothing left ...
138 : : */
139 : 3956 : return ExecClearTuple(slot);
140 : : }
141 : :
142 : : /*
143 : : * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
144 : : */
145 : : static bool
6035 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 *
3214 andres@anarazel.de 161 : 249517 : ExecCteScan(PlanState *pstate)
162 : : {
163 : 249517 : CteScanState *node = castNode(CteScanState, pstate);
164 : :
6035 tgl@sss.pgh.pa.us 165 : 249517 : return ExecScan(&node->ss,
166 : : (ExecScanAccessMtd) CteScanNext,
167 : : (ExecScanRecheckMtd) CteScanRecheck);
168 : : }
169 : :
170 : :
171 : : /* ----------------------------------------------------------------
172 : : * ExecInitCteScan
173 : : * ----------------------------------------------------------------
174 : : */
175 : : CteScanState *
6422 176 : 2675 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
177 : : {
178 : : CteScanState *scanstate;
179 : : ParamExecData *prmdata;
180 : :
181 : : /* check for unsupported flags */
182 [ - + ]: 2675 : 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 : 2675 : eflags |= EXEC_FLAG_REWIND;
197 : :
198 : : /*
199 : : * CteScan should not have any children.
200 : : */
201 [ - + ]: 2675 : Assert(outerPlan(node) == NULL);
202 [ - + ]: 2675 : Assert(innerPlan(node) == NULL);
203 : :
204 : : /*
205 : : * create new CteScanState for node
206 : : */
207 : 2675 : scanstate = makeNode(CteScanState);
208 : 2675 : scanstate->ss.ps.plan = (Plan *) node;
209 : 2675 : scanstate->ss.ps.state = estate;
3214 andres@anarazel.de 210 : 2675 : scanstate->ss.ps.ExecProcNode = ExecCteScan;
6422 tgl@sss.pgh.pa.us 211 : 2675 : scanstate->eflags = eflags;
212 : 2675 : scanstate->cte_table = NULL;
213 : 2675 : scanstate->eof_cte = false;
214 : :
215 : : /*
216 : : * Find the already-initialized plan for the CTE query.
217 : : */
218 : 5350 : scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
219 : 2675 : 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 : 2675 : prmdata = &(estate->es_param_exec_vals[node->cteParam]);
228 [ - + ]: 2675 : Assert(prmdata->execPlan == NULL);
229 [ - + ]: 2675 : Assert(!prmdata->isnull);
3386 andres@anarazel.de 230 : 2675 : scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
6422 tgl@sss.pgh.pa.us 231 [ + + ]: 2675 : if (scanstate->leader == NULL)
232 : : {
233 : : /* I am the leader */
234 : 1624 : prmdata->value = PointerGetDatum(scanstate);
235 : 1624 : scanstate->leader = scanstate;
236 : 1624 : scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
237 : 1624 : tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
238 : 1624 : scanstate->readptr = 0;
239 : : }
240 : : else
241 : : {
242 : : /* Not the leader */
243 : : /* Create my own read pointer, and ensure it is at start */
244 : 1051 : scanstate->readptr =
245 : 1051 : tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
246 : : scanstate->eflags);
3512 247 : 1051 : tuplestore_select_read_pointer(scanstate->leader->cte_table,
248 : : scanstate->readptr);
249 : 1051 : tuplestore_rescan(scanstate->leader->cte_table);
250 : : }
251 : :
252 : : /*
253 : : * Miscellaneous initialization
254 : : *
255 : : * create expression context for node
256 : : */
6422 257 : 2675 : 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 : : */
3000 andres@anarazel.de 263 : 2675 : ExecInitScanTupleSlot(estate, &scanstate->ss,
264 : : ExecGetResultType(scanstate->cteplanstate),
265 : : &TTSOpsMinimalTuple, 0);
266 : :
267 : : /*
268 : : * Initialize result type and projection.
269 : : */
2734 270 : 2675 : ExecInitResultTypeTL(&scanstate->ss.ps);
6422 tgl@sss.pgh.pa.us 271 : 2675 : ExecAssignScanProjectionInfo(&scanstate->ss);
272 : :
273 : : /*
274 : : * initialize child expressions
275 : : */
3000 andres@anarazel.de 276 : 2675 : scanstate->ss.ps.qual =
277 : 2675 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
278 : :
6422 tgl@sss.pgh.pa.us 279 : 2675 : return scanstate;
280 : : }
281 : :
282 : : /* ----------------------------------------------------------------
283 : : * ExecEndCteScan
284 : : *
285 : : * frees any storage allocated through C routines.
286 : : * ----------------------------------------------------------------
287 : : */
288 : : void
289 : 2659 : ExecEndCteScan(CteScanState *node)
290 : : {
291 : : /*
292 : : * If I am the leader, free the tuplestore.
293 : : */
294 [ + + ]: 2659 : if (node->leader == node)
295 : : {
296 : 1609 : tuplestore_end(node->cte_table);
5011 297 : 1609 : node->cte_table = NULL;
298 : : }
6422 299 : 2659 : }
300 : :
301 : : /* ----------------------------------------------------------------
302 : : * ExecReScanCteScan
303 : : *
304 : : * Rescans the relation.
305 : : * ----------------------------------------------------------------
306 : : */
307 : : void
5776 308 : 3401 : ExecReScanCteScan(CteScanState *node)
309 : : {
6403 310 : 3401 : Tuplestorestate *tuplestorestate = node->leader->cte_table;
311 : :
2734 andres@anarazel.de 312 [ + + ]: 3401 : if (node->ss.ps.ps_ResultTupleSlot)
313 : 121 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
314 : :
6035 tgl@sss.pgh.pa.us 315 : 3401 : 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 : : */
5011 325 [ + + ]: 3401 : 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 : : */
6422 337 : 3097 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
338 : 3097 : tuplestore_rescan(tuplestorestate);
339 : : }
340 : 3401 : }
|