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-2024, 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 *
5671 tgl@sss.pgh.pa.us 31 :CBC 147890 : CteScanNext(CteScanState *node)
32 : : {
33 : : EState *estate;
34 : : ScanDirection dir;
35 : : bool forward;
36 : : Tuplestorestate *tuplestorestate;
37 : : bool eof_tuplestore;
38 : : TupleTableSlot *slot;
39 : :
40 : : /*
41 : : * get state info from node
42 : : */
43 : 147890 : estate = node->ss.ps.state;
44 : 147890 : dir = estate->es_direction;
45 : 147890 : forward = ScanDirectionIsForward(dir);
46 : 147890 : tuplestorestate = node->leader->cte_table;
47 : 147890 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
48 : 147890 : 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 : 147890 : eof_tuplestore = tuplestore_ateof(tuplestorestate);
55 : :
56 [ - + - - ]: 147890 : if (!forward && eof_tuplestore)
57 : : {
5671 tgl@sss.pgh.pa.us 58 [ # # ]:UBC 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 : : }
69 : 0 : eof_tuplestore = false;
70 : : }
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 : : */
5671 tgl@sss.pgh.pa.us 79 [ + + ]:CBC 147890 : if (!eof_tuplestore)
80 : : {
5497 81 [ + + ]: 63757 : if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
5671 82 : 59772 : return slot;
83 [ + - ]: 3985 : if (forward)
84 : 3985 : eof_tuplestore = true;
85 : : }
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 [ + - + + ]: 88118 : if (eof_tuplestore && !node->leader->eof_cte)
96 : : {
97 : : 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 : 85196 : cteslot = ExecProcNode(node->cteplanstate);
104 [ + + + + ]: 85187 : if (TupIsNull(cteslot))
105 : : {
106 : 983 : node->leader->eof_cte = true;
107 : 983 : 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 : : */
2246 115 : 84204 : 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 : : */
5671 124 : 84204 : 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 : 84204 : return ExecCopySlot(slot, cteslot);
133 : : }
134 : :
135 : : /*
136 : : * Nothing left ...
137 : : */
138 : 2922 : return ExecClearTuple(slot);
139 : : }
140 : :
141 : : /*
142 : : * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
143 : : */
144 : : static bool
5284 145 : 2 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
146 : : {
147 : : /* nothing to check */
148 : 2 : 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 *
2463 andres@anarazel.de 160 : 143406 : ExecCteScan(PlanState *pstate)
161 : : {
162 : 143406 : CteScanState *node = castNode(CteScanState, pstate);
163 : :
5284 tgl@sss.pgh.pa.us 164 : 143406 : return ExecScan(&node->ss,
165 : : (ExecScanAccessMtd) CteScanNext,
166 : : (ExecScanRecheckMtd) CteScanRecheck);
167 : : }
168 : :
169 : :
170 : : /* ----------------------------------------------------------------
171 : : * ExecInitCteScan
172 : : * ----------------------------------------------------------------
173 : : */
174 : : CteScanState *
5671 175 : 1605 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
176 : : {
177 : : CteScanState *scanstate;
178 : : ParamExecData *prmdata;
179 : :
180 : : /* check for unsupported flags */
181 [ - + ]: 1605 : 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 : 1605 : eflags |= EXEC_FLAG_REWIND;
196 : :
197 : : /*
198 : : * CteScan should not have any children.
199 : : */
200 [ - + ]: 1605 : Assert(outerPlan(node) == NULL);
201 [ - + ]: 1605 : Assert(innerPlan(node) == NULL);
202 : :
203 : : /*
204 : : * create new CteScanState for node
205 : : */
206 : 1605 : scanstate = makeNode(CteScanState);
207 : 1605 : scanstate->ss.ps.plan = (Plan *) node;
208 : 1605 : scanstate->ss.ps.state = estate;
2463 andres@anarazel.de 209 : 1605 : scanstate->ss.ps.ExecProcNode = ExecCteScan;
5671 tgl@sss.pgh.pa.us 210 : 1605 : scanstate->eflags = eflags;
211 : 1605 : scanstate->cte_table = NULL;
212 : 1605 : scanstate->eof_cte = false;
213 : :
214 : : /*
215 : : * Find the already-initialized plan for the CTE query.
216 : : */
217 : 3210 : scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
218 : 1605 : 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 : 1605 : prmdata = &(estate->es_param_exec_vals[node->cteParam]);
227 [ - + ]: 1605 : Assert(prmdata->execPlan == NULL);
228 [ - + ]: 1605 : Assert(!prmdata->isnull);
2635 andres@anarazel.de 229 : 1605 : scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
5671 tgl@sss.pgh.pa.us 230 [ + + ]: 1605 : if (scanstate->leader == NULL)
231 : : {
232 : : /* I am the leader */
233 : 998 : prmdata->value = PointerGetDatum(scanstate);
234 : 998 : scanstate->leader = scanstate;
235 : 998 : scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
236 : 998 : tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
237 : 998 : scanstate->readptr = 0;
238 : : }
239 : : else
240 : : {
241 : : /* Not the leader */
242 : : /* Create my own read pointer, and ensure it is at start */
243 : 607 : scanstate->readptr =
244 : 607 : tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
245 : : scanstate->eflags);
2761 246 : 607 : tuplestore_select_read_pointer(scanstate->leader->cte_table,
247 : : scanstate->readptr);
248 : 607 : tuplestore_rescan(scanstate->leader->cte_table);
249 : : }
250 : :
251 : : /*
252 : : * Miscellaneous initialization
253 : : *
254 : : * create expression context for node
255 : : */
5671 256 : 1605 : 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 : : */
2249 andres@anarazel.de 262 : 1605 : ExecInitScanTupleSlot(estate, &scanstate->ss,
263 : : ExecGetResultType(scanstate->cteplanstate),
264 : : &TTSOpsMinimalTuple);
265 : :
266 : : /*
267 : : * Initialize result type and projection.
268 : : */
1983 269 : 1605 : ExecInitResultTypeTL(&scanstate->ss.ps);
5671 tgl@sss.pgh.pa.us 270 : 1605 : ExecAssignScanProjectionInfo(&scanstate->ss);
271 : :
272 : : /*
273 : : * initialize child expressions
274 : : */
2249 andres@anarazel.de 275 : 1605 : scanstate->ss.ps.qual =
276 : 1605 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
277 : :
5671 tgl@sss.pgh.pa.us 278 : 1605 : return scanstate;
279 : : }
280 : :
281 : : /* ----------------------------------------------------------------
282 : : * ExecEndCteScan
283 : : *
284 : : * frees any storage allocated through C routines.
285 : : * ----------------------------------------------------------------
286 : : */
287 : : void
288 : 1592 : ExecEndCteScan(CteScanState *node)
289 : : {
290 : : /*
291 : : * If I am the leader, free the tuplestore.
292 : : */
293 [ + + ]: 1592 : if (node->leader == node)
294 : : {
295 : 986 : tuplestore_end(node->cte_table);
4260 296 : 986 : node->cte_table = NULL;
297 : : }
5671 298 : 1592 : }
299 : :
300 : : /* ----------------------------------------------------------------
301 : : * ExecReScanCteScan
302 : : *
303 : : * Rescans the relation.
304 : : * ----------------------------------------------------------------
305 : : */
306 : : void
5025 307 : 2619 : ExecReScanCteScan(CteScanState *node)
308 : : {
5652 309 : 2619 : Tuplestorestate *tuplestorestate = node->leader->cte_table;
310 : :
1983 andres@anarazel.de 311 [ + + ]: 2619 : if (node->ss.ps.ps_ResultTupleSlot)
312 : 97 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
313 : :
5284 tgl@sss.pgh.pa.us 314 : 2619 : 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 : : */
4260 324 [ + + ]: 2619 : if (node->leader->cteplanstate->chgParam != NULL)
325 : : {
326 : 148 : tuplestore_clear(tuplestorestate);
327 : 148 : node->leader->eof_cte = false;
328 : : }
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 : : */
5671 336 : 2471 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
337 : 2471 : tuplestore_rescan(tuplestorestate);
338 : : }
339 : 2619 : }
|