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 *
31 299764 : 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 299764 : estate = node->ss.ps.state;
44 299764 : dir = estate->es_direction;
45 299764 : forward = ScanDirectionIsForward(dir);
46 299764 : tuplestorestate = node->leader->cte_table;
47 299764 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
48 299764 : 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 299764 : eof_tuplestore = tuplestore_ateof(tuplestorestate);
55 :
56 299764 : 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 : }
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 : */
79 299764 : if (!eof_tuplestore)
80 : {
81 131200 : if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
82 122578 : return slot;
83 8622 : if (forward)
84 8622 : 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 177186 : 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 170692 : cteslot = ExecProcNode(node->cteplanstate);
104 170674 : if (TupIsNull(cteslot))
105 : {
106 1962 : node->leader->eof_cte = true;
107 1962 : 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 168712 : 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 168712 : 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 168712 : return ExecCopySlot(slot, cteslot);
133 : }
134 :
135 : /*
136 : * Nothing left ...
137 : */
138 6494 : return ExecClearTuple(slot);
139 : }
140 :
141 : /*
142 : * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
143 : */
144 : static bool
145 4 : CteScanRecheck(CteScanState *node, TupleTableSlot *slot)
146 : {
147 : /* nothing to check */
148 4 : 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 289604 : ExecCteScan(PlanState *pstate)
161 : {
162 289604 : CteScanState *node = castNode(CteScanState, pstate);
163 :
164 289604 : return ExecScan(&node->ss,
165 : (ExecScanAccessMtd) CteScanNext,
166 : (ExecScanRecheckMtd) CteScanRecheck);
167 : }
168 :
169 :
170 : /* ----------------------------------------------------------------
171 : * ExecInitCteScan
172 : * ----------------------------------------------------------------
173 : */
174 : CteScanState *
175 3200 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
176 : {
177 : CteScanState *scanstate;
178 : ParamExecData *prmdata;
179 :
180 : /* check for unsupported flags */
181 : 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 3200 : eflags |= EXEC_FLAG_REWIND;
196 :
197 : /*
198 : * CteScan should not have any children.
199 : */
200 : Assert(outerPlan(node) == NULL);
201 : Assert(innerPlan(node) == NULL);
202 :
203 : /*
204 : * create new CteScanState for node
205 : */
206 3200 : scanstate = makeNode(CteScanState);
207 3200 : scanstate->ss.ps.plan = (Plan *) node;
208 3200 : scanstate->ss.ps.state = estate;
209 3200 : scanstate->ss.ps.ExecProcNode = ExecCteScan;
210 3200 : scanstate->eflags = eflags;
211 3200 : scanstate->cte_table = NULL;
212 3200 : scanstate->eof_cte = false;
213 :
214 : /*
215 : * Find the already-initialized plan for the CTE query.
216 : */
217 6400 : scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
218 3200 : 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 3200 : prmdata = &(estate->es_param_exec_vals[node->cteParam]);
227 : Assert(prmdata->execPlan == NULL);
228 : Assert(!prmdata->isnull);
229 3200 : scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
230 3200 : if (scanstate->leader == NULL)
231 : {
232 : /* I am the leader */
233 1988 : prmdata->value = PointerGetDatum(scanstate);
234 1988 : scanstate->leader = scanstate;
235 1988 : scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
236 1988 : tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
237 1988 : scanstate->readptr = 0;
238 : }
239 : else
240 : {
241 : /* Not the leader */
242 : /* Create my own read pointer, and ensure it is at start */
243 1212 : scanstate->readptr =
244 1212 : tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
245 : scanstate->eflags);
246 1212 : tuplestore_select_read_pointer(scanstate->leader->cte_table,
247 : scanstate->readptr);
248 1212 : tuplestore_rescan(scanstate->leader->cte_table);
249 : }
250 :
251 : /*
252 : * Miscellaneous initialization
253 : *
254 : * create expression context for node
255 : */
256 3200 : 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 3200 : ExecInitScanTupleSlot(estate, &scanstate->ss,
263 : ExecGetResultType(scanstate->cteplanstate),
264 : &TTSOpsMinimalTuple);
265 :
266 : /*
267 : * Initialize result type and projection.
268 : */
269 3200 : ExecInitResultTypeTL(&scanstate->ss.ps);
270 3200 : ExecAssignScanProjectionInfo(&scanstate->ss);
271 :
272 : /*
273 : * initialize child expressions
274 : */
275 3200 : scanstate->ss.ps.qual =
276 3200 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
277 :
278 3200 : return scanstate;
279 : }
280 :
281 : /* ----------------------------------------------------------------
282 : * ExecEndCteScan
283 : *
284 : * frees any storage allocated through C routines.
285 : * ----------------------------------------------------------------
286 : */
287 : void
288 3174 : ExecEndCteScan(CteScanState *node)
289 : {
290 : /*
291 : * If I am the leader, free the tuplestore.
292 : */
293 3174 : if (node->leader == node)
294 : {
295 1964 : tuplestore_end(node->cte_table);
296 1964 : node->cte_table = NULL;
297 : }
298 3174 : }
299 :
300 : /* ----------------------------------------------------------------
301 : * ExecReScanCteScan
302 : *
303 : * Rescans the relation.
304 : * ----------------------------------------------------------------
305 : */
306 : void
307 5906 : ExecReScanCteScan(CteScanState *node)
308 : {
309 5906 : Tuplestorestate *tuplestorestate = node->leader->cte_table;
310 :
311 5906 : if (node->ss.ps.ps_ResultTupleSlot)
312 194 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
313 :
314 5906 : 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 5906 : if (node->leader->cteplanstate->chgParam != NULL)
325 : {
326 296 : tuplestore_clear(tuplestorestate);
327 296 : 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 : */
336 5610 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
337 5610 : tuplestore_rescan(tuplestorestate);
338 : }
339 5906 : }
|