Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * nodeMaterial.c
4 : * Routines to handle materialization 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/nodeMaterial.c
12 : *
13 : *-------------------------------------------------------------------------
14 : */
15 : /*
16 : * INTERFACE ROUTINES
17 : * ExecMaterial - materialize the result of a subplan
18 : * ExecInitMaterial - initialize node and subnodes
19 : * ExecEndMaterial - shutdown node and subnodes
20 : *
21 : */
22 : #include "postgres.h"
23 :
24 : #include "executor/executor.h"
25 : #include "executor/nodeMaterial.h"
26 : #include "miscadmin.h"
27 : #include "utils/tuplestore.h"
28 :
29 : /* ----------------------------------------------------------------
30 : * ExecMaterial
31 : *
32 : * As long as we are at the end of the data collected in the tuplestore,
33 : * we collect one new row from the subplan on each call, and stash it
34 : * aside in the tuplestore before returning it. The tuplestore is
35 : * only read if we are asked to scan backwards, rescan, or mark/restore.
36 : *
37 : * ----------------------------------------------------------------
38 : */
39 : static TupleTableSlot * /* result tuple from subplan */
40 2656384 : ExecMaterial(PlanState *pstate)
41 : {
42 2656384 : MaterialState *node = castNode(MaterialState, pstate);
43 : EState *estate;
44 : ScanDirection dir;
45 : bool forward;
46 : Tuplestorestate *tuplestorestate;
47 : bool eof_tuplestore;
48 : TupleTableSlot *slot;
49 :
50 2656384 : CHECK_FOR_INTERRUPTS();
51 :
52 : /*
53 : * get state info from node
54 : */
55 2656384 : estate = node->ss.ps.state;
56 2656384 : dir = estate->es_direction;
57 2656384 : forward = ScanDirectionIsForward(dir);
58 2656384 : tuplestorestate = node->tuplestorestate;
59 :
60 : /*
61 : * If first time through, and we need a tuplestore, initialize it.
62 : */
63 2656384 : if (tuplestorestate == NULL && node->eflags != 0)
64 : {
65 1861 : tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
66 1861 : tuplestore_set_eflags(tuplestorestate, node->eflags);
67 1861 : if (node->eflags & EXEC_FLAG_MARK)
68 : {
69 : /*
70 : * Allocate a second read pointer to serve as the mark. We know it
71 : * must have index 1, so needn't store that.
72 : */
73 : int ptrno PG_USED_FOR_ASSERTS_ONLY;
74 :
75 79 : ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
76 : node->eflags);
77 : Assert(ptrno == 1);
78 : }
79 1861 : node->tuplestorestate = tuplestorestate;
80 : }
81 :
82 : /*
83 : * If we are not at the end of the tuplestore, or are going backwards, try
84 : * to fetch a tuple from tuplestore.
85 : */
86 5312703 : eof_tuplestore = (tuplestorestate == NULL) ||
87 2656319 : tuplestore_ateof(tuplestorestate);
88 :
89 2656384 : if (!forward && eof_tuplestore)
90 : {
91 8 : if (!node->eof_underlying)
92 : {
93 : /*
94 : * When reversing direction at tuplestore EOF, the first
95 : * gettupleslot call will fetch the last-added tuple; but we want
96 : * to return the one before that, if possible. So do an extra
97 : * fetch.
98 : */
99 0 : if (!tuplestore_advance(tuplestorestate, forward))
100 0 : return NULL; /* the tuplestore must be empty */
101 : }
102 8 : eof_tuplestore = false;
103 : }
104 :
105 : /*
106 : * If we can fetch another tuple from the tuplestore, return it.
107 : */
108 2656384 : slot = node->ss.ps.ps_ResultTupleSlot;
109 2656384 : if (!eof_tuplestore)
110 : {
111 2604292 : if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))
112 2518459 : return slot;
113 85833 : if (forward)
114 85825 : eof_tuplestore = true;
115 : }
116 :
117 : /*
118 : * If necessary, try to fetch another row from the subplan.
119 : *
120 : * Note: the eof_underlying state variable exists to short-circuit further
121 : * subplan calls. It's not optional, unfortunately, because some plan
122 : * node types are not robust about being called again when they've already
123 : * returned NULL.
124 : */
125 137925 : if (eof_tuplestore && !node->eof_underlying)
126 : {
127 : PlanState *outerNode;
128 : TupleTableSlot *outerslot;
129 :
130 : /*
131 : * We can only get here with forward==true, so no need to worry about
132 : * which direction the subplan will go.
133 : */
134 58051 : outerNode = outerPlanState(node);
135 58051 : outerslot = ExecProcNode(outerNode);
136 58051 : if (TupIsNull(outerslot))
137 : {
138 1702 : node->eof_underlying = true;
139 1702 : return NULL;
140 : }
141 :
142 : /*
143 : * Append a copy of the returned tuple to tuplestore. NOTE: because
144 : * the tuplestore is certainly in EOF state, its read position will
145 : * move forward over the added tuple. This is what we want.
146 : */
147 56349 : if (tuplestorestate)
148 56287 : tuplestore_puttupleslot(tuplestorestate, outerslot);
149 :
150 56349 : ExecCopySlot(slot, outerslot);
151 56349 : return slot;
152 : }
153 :
154 : /*
155 : * Nothing left ...
156 : */
157 79874 : return ExecClearTuple(slot);
158 : }
159 :
160 : /* ----------------------------------------------------------------
161 : * ExecInitMaterial
162 : * ----------------------------------------------------------------
163 : */
164 : MaterialState *
165 2787 : ExecInitMaterial(Material *node, EState *estate, int eflags)
166 : {
167 : MaterialState *matstate;
168 : Plan *outerPlan;
169 :
170 : /*
171 : * create state structure
172 : */
173 2787 : matstate = makeNode(MaterialState);
174 2787 : matstate->ss.ps.plan = (Plan *) node;
175 2787 : matstate->ss.ps.state = estate;
176 2787 : matstate->ss.ps.ExecProcNode = ExecMaterial;
177 :
178 : /*
179 : * We must have a tuplestore buffering the subplan output to do backward
180 : * scan or mark/restore. We also prefer to materialize the subplan output
181 : * if we might be called on to rewind and replay it many times. However,
182 : * if none of these cases apply, we can skip storing the data.
183 : */
184 2787 : matstate->eflags = (eflags & (EXEC_FLAG_REWIND |
185 : EXEC_FLAG_BACKWARD |
186 : EXEC_FLAG_MARK));
187 :
188 : /*
189 : * Tuplestore's interpretation of the flag bits is subtly different from
190 : * the general executor meaning: it doesn't think BACKWARD necessarily
191 : * means "backwards all the way to start". If told to support BACKWARD we
192 : * must include REWIND in the tuplestore eflags, else tuplestore_trim
193 : * might throw away too much.
194 : */
195 2787 : if (eflags & EXEC_FLAG_BACKWARD)
196 13 : matstate->eflags |= EXEC_FLAG_REWIND;
197 :
198 2787 : matstate->eof_underlying = false;
199 2787 : matstate->tuplestorestate = NULL;
200 :
201 : /*
202 : * Miscellaneous initialization
203 : *
204 : * Materialization nodes don't need ExprContexts because they never call
205 : * ExecQual or ExecProject.
206 : */
207 :
208 : /*
209 : * initialize child nodes
210 : *
211 : * We shield the child node from the need to support REWIND, BACKWARD, or
212 : * MARK/RESTORE.
213 : */
214 2787 : eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
215 :
216 2787 : outerPlan = outerPlan(node);
217 2787 : outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags);
218 :
219 : /*
220 : * Initialize result type and slot. No need to initialize projection info
221 : * because this node doesn't do projections.
222 : *
223 : * material nodes only return tuples from their materialized relation.
224 : */
225 2787 : ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple);
226 2787 : matstate->ss.ps.ps_ProjInfo = NULL;
227 :
228 : /*
229 : * initialize tuple type.
230 : */
231 2787 : ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple);
232 :
233 2787 : return matstate;
234 : }
235 :
236 : /* ----------------------------------------------------------------
237 : * ExecEndMaterial
238 : * ----------------------------------------------------------------
239 : */
240 : void
241 2745 : ExecEndMaterial(MaterialState *node)
242 : {
243 : /*
244 : * Release tuplestore resources
245 : */
246 2745 : if (node->tuplestorestate != NULL)
247 1814 : tuplestore_end(node->tuplestorestate);
248 2745 : node->tuplestorestate = NULL;
249 :
250 : /*
251 : * shut down the subplan
252 : */
253 2745 : ExecEndNode(outerPlanState(node));
254 2745 : }
255 :
256 : /* ----------------------------------------------------------------
257 : * ExecMaterialMarkPos
258 : *
259 : * Calls tuplestore to save the current position in the stored file.
260 : * ----------------------------------------------------------------
261 : */
262 : void
263 4332 : ExecMaterialMarkPos(MaterialState *node)
264 : {
265 : Assert(node->eflags & EXEC_FLAG_MARK);
266 :
267 : /*
268 : * if we haven't materialized yet, just return.
269 : */
270 4332 : if (!node->tuplestorestate)
271 8 : return;
272 :
273 : /*
274 : * copy the active read pointer to the mark.
275 : */
276 4324 : tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1);
277 :
278 : /*
279 : * since we may have advanced the mark, try to truncate the tuplestore.
280 : */
281 4324 : tuplestore_trim(node->tuplestorestate);
282 : }
283 :
284 : /* ----------------------------------------------------------------
285 : * ExecMaterialRestrPos
286 : *
287 : * Calls tuplestore to restore the last saved file position.
288 : * ----------------------------------------------------------------
289 : */
290 : void
291 36020 : ExecMaterialRestrPos(MaterialState *node)
292 : {
293 : Assert(node->eflags & EXEC_FLAG_MARK);
294 :
295 : /*
296 : * if we haven't materialized yet, just return.
297 : */
298 36020 : if (!node->tuplestorestate)
299 0 : return;
300 :
301 : /*
302 : * copy the mark to the active read pointer.
303 : */
304 36020 : tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0);
305 : }
306 :
307 : /* ----------------------------------------------------------------
308 : * ExecReScanMaterial
309 : *
310 : * Rescans the materialized relation.
311 : * ----------------------------------------------------------------
312 : */
313 : void
314 87277 : ExecReScanMaterial(MaterialState *node)
315 : {
316 87277 : PlanState *outerPlan = outerPlanState(node);
317 :
318 87277 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
319 :
320 87277 : if (node->eflags != 0)
321 : {
322 : /*
323 : * If we haven't materialized yet, just return. If outerplan's
324 : * chgParam is not NULL then it will be re-scanned by ExecProcNode,
325 : * else no reason to re-scan it at all.
326 : */
327 87275 : if (!node->tuplestorestate)
328 1769 : return;
329 :
330 : /*
331 : * If subnode is to be rescanned then we forget previous stored
332 : * results; we have to re-read the subplan and re-store. Also, if we
333 : * told tuplestore it needn't support rescan, we lose and must
334 : * re-read. (This last should not happen in common cases; else our
335 : * caller lied by not passing EXEC_FLAG_REWIND to us.)
336 : *
337 : * Otherwise we can just rewind and rescan the stored output. The
338 : * state of the subnode does not change.
339 : */
340 85506 : if (outerPlan->chgParam != NULL ||
341 85498 : (node->eflags & EXEC_FLAG_REWIND) == 0)
342 : {
343 8 : tuplestore_end(node->tuplestorestate);
344 8 : node->tuplestorestate = NULL;
345 8 : if (outerPlan->chgParam == NULL)
346 0 : ExecReScan(outerPlan);
347 8 : node->eof_underlying = false;
348 : }
349 : else
350 85498 : tuplestore_rescan(node->tuplestorestate);
351 : }
352 : else
353 : {
354 : /* In this case we are just passing on the subquery's output */
355 :
356 : /*
357 : * if chgParam of subnode is not null then plan will be re-scanned by
358 : * first ExecProcNode.
359 : */
360 2 : if (outerPlan->chgParam == NULL)
361 0 : ExecReScan(outerPlan);
362 2 : node->eof_underlying = false;
363 : }
364 : }
|