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