Age Owner Branch data TLA 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 */
3214 andres@anarazel.de 40 :CBC 2975594 : ExecMaterial(PlanState *pstate)
41 : : {
42 : 2975594 : 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 : :
3206 50 [ + + ]: 2975594 : CHECK_FOR_INTERRUPTS();
51 : :
52 : : /*
53 : : * get state info from node
54 : : */
8552 tgl@sss.pgh.pa.us 55 : 2975594 : estate = node->ss.ps.state;
10467 bruce@momjian.us 56 : 2975594 : dir = estate->es_direction;
8458 tgl@sss.pgh.pa.us 57 : 2975594 : forward = ScanDirectionIsForward(dir);
6425 58 : 2975594 : tuplestorestate = node->tuplestorestate;
59 : :
60 : : /*
61 : : * If first time through, and we need a tuplestore, initialize it.
62 : : */
6924 63 [ + + + + ]: 2975594 : if (tuplestorestate == NULL && node->eflags != 0)
64 : : {
8127 65 : 1899 : tuplestorestate = tuplestore_begin_heap(true, false, work_mem);
6924 66 : 1899 : tuplestore_set_eflags(tuplestorestate, node->eflags);
6425 67 [ + + ]: 1899 : 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 : :
6338 75 : 79 : ptrno = tuplestore_alloc_read_pointer(tuplestorestate,
76 : : node->eflags);
77 [ - + ]: 79 : Assert(ptrno == 1);
78 : : }
6425 79 : 1899 : 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 : : */
7371 86 [ + + + + ]: 5951123 : eof_tuplestore = (tuplestorestate == NULL) ||
87 : 2975529 : tuplestore_ateof(tuplestorestate);
88 : :
8458 89 [ + + + + ]: 2975594 : 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 : : */
7252 tgl@sss.pgh.pa.us 99 [ # # ]:UBC 0 : if (!tuplestore_advance(tuplestorestate, forward))
8310 bruce@momjian.us 100 : 0 : return NULL; /* the tuplestore must be empty */
101 : : }
8458 tgl@sss.pgh.pa.us 102 :CBC 8 : eof_tuplestore = false;
103 : : }
104 : :
105 : : /*
106 : : * If we can fetch another tuple from the tuplestore, return it.
107 : : */
7252 108 : 2975594 : slot = node->ss.ps.ps_ResultTupleSlot;
8458 109 [ + + ]: 2975594 : if (!eof_tuplestore)
110 : : {
6248 111 [ + + ]: 2915102 : if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot))
7252 112 : 2827120 : return slot;
113 [ + + ]: 87982 : if (forward)
8458 114 : 87974 : 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 [ + + + + ]: 148474 : 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 : 66485 : outerNode = outerPlanState(node);
135 : 66485 : outerslot = ExecProcNode(outerNode);
136 [ + + + + ]: 66485 : if (TupIsNull(outerslot))
137 : : {
138 : 1742 : node->eof_underlying = true;
139 : 1742 : 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 : : */
7371 147 [ + + ]: 64743 : if (tuplestorestate)
7252 148 : 64681 : tuplestore_puttupleslot(tuplestorestate, outerslot);
149 : :
2728 andres@anarazel.de 150 : 64743 : ExecCopySlot(slot, outerslot);
151 : 64743 : return slot;
152 : : }
153 : :
154 : : /*
155 : : * Nothing left ...
156 : : */
7252 tgl@sss.pgh.pa.us 157 : 81989 : return ExecClearTuple(slot);
158 : : }
159 : :
160 : : /* ----------------------------------------------------------------
161 : : * ExecInitMaterial
162 : : * ----------------------------------------------------------------
163 : : */
164 : : MaterialState *
7371 165 : 2832 : ExecInitMaterial(Material *node, EState *estate, int eflags)
166 : : {
167 : : MaterialState *matstate;
168 : : Plan *outerPlan;
169 : :
170 : : /*
171 : : * create state structure
172 : : */
10467 bruce@momjian.us 173 : 2832 : matstate = makeNode(MaterialState);
8552 tgl@sss.pgh.pa.us 174 : 2832 : matstate->ss.ps.plan = (Plan *) node;
175 : 2832 : matstate->ss.ps.state = estate;
3214 andres@anarazel.de 176 : 2832 : 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 : : */
6924 tgl@sss.pgh.pa.us 184 : 2832 : 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 : : */
6338 195 [ + + ]: 2832 : if (eflags & EXEC_FLAG_BACKWARD)
196 : 13 : matstate->eflags |= EXEC_FLAG_REWIND;
197 : :
8458 198 : 2832 : matstate->eof_underlying = false;
7371 199 : 2832 : 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 : 2832 : eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK);
215 : :
8552 216 : 2832 : outerPlan = outerPlan(node);
7371 217 : 2832 : 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 : : */
2728 andres@anarazel.de 225 : 2832 : ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple);
8552 tgl@sss.pgh.pa.us 226 : 2832 : matstate->ss.ps.ps_ProjInfo = NULL;
227 : :
228 : : /*
229 : : * initialize tuple type.
230 : : */
2728 andres@anarazel.de 231 : 2832 : ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple);
232 : :
8552 tgl@sss.pgh.pa.us 233 : 2832 : return matstate;
234 : : }
235 : :
236 : : /* ----------------------------------------------------------------
237 : : * ExecEndMaterial
238 : : * ----------------------------------------------------------------
239 : : */
240 : : void
241 : 2790 : ExecEndMaterial(MaterialState *node)
242 : : {
243 : : /*
244 : : * Release tuplestore resources
245 : : */
246 [ + + ]: 2790 : if (node->tuplestorestate != NULL)
6425 247 : 1849 : tuplestore_end(node->tuplestorestate);
8552 248 : 2790 : node->tuplestorestate = NULL;
249 : :
250 : : /*
251 : : * shut down the subplan
252 : : */
8542 253 : 2790 : ExecEndNode(outerPlanState(node));
10467 bruce@momjian.us 254 : 2790 : }
255 : :
256 : : /* ----------------------------------------------------------------
257 : : * ExecMaterialMarkPos
258 : : *
259 : : * Calls tuplestore to save the current position in the stored file.
260 : : * ----------------------------------------------------------------
261 : : */
262 : : void
8552 tgl@sss.pgh.pa.us 263 : 4332 : ExecMaterialMarkPos(MaterialState *node)
264 : : {
6924 265 [ - + ]: 4332 : Assert(node->eflags & EXEC_FLAG_MARK);
266 : :
267 : : /*
268 : : * if we haven't materialized yet, just return.
269 : : */
8552 270 [ + + ]: 4332 : if (!node->tuplestorestate)
10308 vadim4o@yahoo.com 271 : 8 : return;
272 : :
273 : : /*
274 : : * copy the active read pointer to the mark.
275 : : */
6425 tgl@sss.pgh.pa.us 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 : : */
6338 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
8552 291 : 36020 : ExecMaterialRestrPos(MaterialState *node)
292 : : {
6924 293 [ - + ]: 36020 : Assert(node->eflags & EXEC_FLAG_MARK);
294 : :
295 : : /*
296 : : * if we haven't materialized yet, just return.
297 : : */
8552 298 [ - + ]: 36020 : if (!node->tuplestorestate)
9452 tgl@sss.pgh.pa.us 299 :UBC 0 : return;
300 : :
301 : : /*
302 : : * copy the mark to the active read pointer.
303 : : */
6425 tgl@sss.pgh.pa.us 304 :CBC 36020 : tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0);
305 : : }
306 : :
307 : : /* ----------------------------------------------------------------
308 : : * ExecReScanMaterial
309 : : *
310 : : * Rescans the materialized relation.
311 : : * ----------------------------------------------------------------
312 : : */
313 : : void
5776 314 : 89137 : ExecReScanMaterial(MaterialState *node)
315 : : {
4000 bruce@momjian.us 316 : 89137 : PlanState *outerPlan = outerPlanState(node);
317 : :
8552 tgl@sss.pgh.pa.us 318 : 89137 : ExecClearTuple(node->ss.ps.ps_ResultTupleSlot);
319 : :
6924 320 [ + + ]: 89137 : 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 : : */
7371 327 [ + + ]: 89135 : if (!node->tuplestorestate)
328 : 1807 : 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 : : */
4019 rhaas@postgresql.org 340 [ + + ]: 87328 : if (outerPlan->chgParam != NULL ||
6924 tgl@sss.pgh.pa.us 341 [ - + ]: 87320 : (node->eflags & EXEC_FLAG_REWIND) == 0)
342 : : {
6425 343 : 8 : tuplestore_end(node->tuplestorestate);
7371 344 : 8 : node->tuplestorestate = NULL;
4019 rhaas@postgresql.org 345 [ - + ]: 8 : if (outerPlan->chgParam == NULL)
4019 rhaas@postgresql.org 346 :UBC 0 : ExecReScan(outerPlan);
7371 tgl@sss.pgh.pa.us 347 :CBC 8 : node->eof_underlying = false;
348 : : }
349 : : else
6425 350 : 87320 : 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 : : */
4019 rhaas@postgresql.org 360 [ - + ]: 2 : if (outerPlan->chgParam == NULL)
4019 rhaas@postgresql.org 361 :UBC 0 : ExecReScan(outerPlan);
7371 tgl@sss.pgh.pa.us 362 :CBC 2 : node->eof_underlying = false;
363 : : }
364 : : }
|