Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeCtescan.c
4 : * routines to handle CteScan nodes.
5 : *
6 : * Portions Copyright (c) 1996-2023, 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/execdebug.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 *
5300 tgl 31 CBC 110275 : 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 110275 : estate = node->ss.ps.state;
44 110275 : dir = estate->es_direction;
45 110275 : forward = ScanDirectionIsForward(dir);
46 110275 : tuplestorestate = node->leader->cte_table;
47 110275 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
48 110275 : 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 110275 : eof_tuplestore = tuplestore_ateof(tuplestorestate);
55 :
56 110275 : if (!forward && eof_tuplestore)
57 : {
5300 tgl 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 : */
5300 tgl 79 CBC 110275 : if (!eof_tuplestore)
80 : {
5126 81 46277 : if (tuplestore_gettupleslot(tuplestorestate, forward, true, slot))
5300 82 43384 : return slot;
83 2893 : if (forward)
84 2893 : 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 66891 : 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 64853 : cteslot = ExecProcNode(node->cteplanstate);
104 64844 : if (TupIsNull(cteslot))
105 : {
106 775 : node->leader->eof_cte = true;
107 775 : 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 : */
1875 115 64069 : 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 : */
5300 124 64069 : 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 64069 : return ExecCopySlot(slot, cteslot);
133 : }
134 :
135 : /*
136 : * Nothing left ...
137 : */
138 2038 : return ExecClearTuple(slot);
139 : }
140 :
141 : /*
142 : * CteScanRecheck -- access method routine to recheck a tuple in EvalPlanQual
143 : */
144 : static bool
4913 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 *
2092 andres 160 106661 : ExecCteScan(PlanState *pstate)
161 : {
162 106661 : CteScanState *node = castNode(CteScanState, pstate);
163 :
4913 tgl 164 106661 : return ExecScan(&node->ss,
165 : (ExecScanAccessMtd) CteScanNext,
166 : (ExecScanRecheckMtd) CteScanRecheck);
167 : }
168 :
169 :
170 : /* ----------------------------------------------------------------
171 : * ExecInitCteScan
172 : * ----------------------------------------------------------------
173 : */
174 : CteScanState *
5300 175 1242 : ExecInitCteScan(CteScan *node, EState *estate, int eflags)
176 : {
177 : CteScanState *scanstate;
178 : ParamExecData *prmdata;
179 :
180 : /* check for unsupported flags */
181 1242 : 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 1242 : eflags |= EXEC_FLAG_REWIND;
196 :
197 : /*
198 : * CteScan should not have any children.
199 : */
200 1242 : Assert(outerPlan(node) == NULL);
201 1242 : Assert(innerPlan(node) == NULL);
202 :
203 : /*
204 : * create new CteScanState for node
205 : */
206 1242 : scanstate = makeNode(CteScanState);
207 1242 : scanstate->ss.ps.plan = (Plan *) node;
208 1242 : scanstate->ss.ps.state = estate;
2092 andres 209 1242 : scanstate->ss.ps.ExecProcNode = ExecCteScan;
5300 tgl 210 1242 : scanstate->eflags = eflags;
211 1242 : scanstate->cte_table = NULL;
212 1242 : scanstate->eof_cte = false;
213 :
214 : /*
215 : * Find the already-initialized plan for the CTE query.
216 : */
217 2484 : scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates,
218 1242 : 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 1242 : prmdata = &(estate->es_param_exec_vals[node->cteParam]);
227 1242 : Assert(prmdata->execPlan == NULL);
228 1242 : Assert(!prmdata->isnull);
2264 andres 229 1242 : scanstate->leader = castNode(CteScanState, DatumGetPointer(prmdata->value));
5300 tgl 230 1242 : if (scanstate->leader == NULL)
231 : {
232 : /* I am the leader */
233 813 : prmdata->value = PointerGetDatum(scanstate);
234 813 : scanstate->leader = scanstate;
235 813 : scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem);
236 813 : tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags);
237 813 : scanstate->readptr = 0;
238 : }
239 : else
240 : {
241 : /* Not the leader */
242 : /* Create my own read pointer, and ensure it is at start */
243 429 : scanstate->readptr =
244 429 : tuplestore_alloc_read_pointer(scanstate->leader->cte_table,
245 : scanstate->eflags);
2390 246 429 : tuplestore_select_read_pointer(scanstate->leader->cte_table,
247 : scanstate->readptr);
248 429 : tuplestore_rescan(scanstate->leader->cte_table);
249 : }
250 :
251 : /*
252 : * Miscellaneous initialization
253 : *
254 : * create expression context for node
255 : */
5300 256 1242 : 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 : */
1878 andres 262 1242 : ExecInitScanTupleSlot(estate, &scanstate->ss,
263 : ExecGetResultType(scanstate->cteplanstate),
264 : &TTSOpsMinimalTuple);
265 :
266 : /*
267 : * Initialize result type and projection.
268 : */
1612 269 1242 : ExecInitResultTypeTL(&scanstate->ss.ps);
5300 tgl 270 1242 : ExecAssignScanProjectionInfo(&scanstate->ss);
271 :
272 : /*
273 : * initialize child expressions
274 : */
1878 andres 275 1242 : scanstate->ss.ps.qual =
276 1242 : ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
277 :
5300 tgl 278 1242 : return scanstate;
279 : }
280 :
281 : /* ----------------------------------------------------------------
282 : * ExecEndCteScan
283 : *
284 : * frees any storage allocated through C routines.
285 : * ----------------------------------------------------------------
286 : */
287 : void
288 1229 : ExecEndCteScan(CteScanState *node)
289 : {
290 : /*
291 : * Free exprcontext
292 : */
293 1229 : ExecFreeExprContext(&node->ss.ps);
294 :
295 : /*
296 : * clean out the tuple table
297 : */
1612 andres 298 1229 : if (node->ss.ps.ps_ResultTupleSlot)
299 488 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
5300 tgl 300 1229 : ExecClearTuple(node->ss.ss_ScanTupleSlot);
301 :
302 : /*
303 : * If I am the leader, free the tuplestore.
304 : */
305 1229 : if (node->leader == node)
306 : {
307 801 : tuplestore_end(node->cte_table);
3889 308 801 : node->cte_table = NULL;
309 : }
5300 310 1229 : }
311 :
312 : /* ----------------------------------------------------------------
313 : * ExecReScanCteScan
314 : *
315 : * Rescans the relation.
316 : * ----------------------------------------------------------------
317 : */
318 : void
4654 319 1861 : ExecReScanCteScan(CteScanState *node)
320 : {
5281 321 1861 : Tuplestorestate *tuplestorestate = node->leader->cte_table;
322 :
1612 andres 323 1861 : if (node->ss.ps.ps_ResultTupleSlot)
324 91 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
325 :
4913 tgl 326 1861 : ExecScanReScan(&node->ss);
327 :
328 : /*
329 : * Clear the tuplestore if a new scan of the underlying CTE is required.
330 : * This implicitly resets all the tuplestore's read pointers. Note that
331 : * multiple CTE nodes might redundantly clear the tuplestore; that's OK,
332 : * and not unduly expensive. We'll stop taking this path as soon as
333 : * somebody has attempted to read something from the underlying CTE
334 : * (thereby causing its chgParam to be cleared).
335 : */
3889 336 1861 : if (node->leader->cteplanstate->chgParam != NULL)
337 : {
338 108 : tuplestore_clear(tuplestorestate);
339 108 : node->leader->eof_cte = false;
340 : }
341 : else
342 : {
343 : /*
344 : * Else, just rewind my own pointer. Either the underlying CTE
345 : * doesn't need a rescan (and we can re-read what's in the tuplestore
346 : * now), or somebody else already took care of it.
347 : */
5300 348 1753 : tuplestore_select_read_pointer(tuplestorestate, node->readptr);
349 1753 : tuplestore_rescan(tuplestorestate);
350 : }
351 1861 : }
|