1 | /*------------------------------------------------------------------------- |
2 | * |
3 | * nodeMaterial.c |
4 | * Routines to handle materialization nodes. |
5 | * |
6 | * Portions Copyright (c) 1996-2019, 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 */ |
39 | ExecMaterial(PlanState *pstate) |
40 | { |
41 | 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 | |
49 | CHECK_FOR_INTERRUPTS(); |
50 | |
51 | /* |
52 | * get state info from node |
53 | */ |
54 | estate = node->ss.ps.state; |
55 | dir = estate->es_direction; |
56 | forward = ScanDirectionIsForward(dir); |
57 | tuplestorestate = node->tuplestorestate; |
58 | |
59 | /* |
60 | * If first time through, and we need a tuplestore, initialize it. |
61 | */ |
62 | if (tuplestorestate == NULL && node->eflags != 0) |
63 | { |
64 | tuplestorestate = tuplestore_begin_heap(true, false, work_mem); |
65 | tuplestore_set_eflags(tuplestorestate, node->eflags); |
66 | 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 | |
74 | ptrno = tuplestore_alloc_read_pointer(tuplestorestate, |
75 | node->eflags); |
76 | Assert(ptrno == 1); |
77 | } |
78 | 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 | */ |
85 | eof_tuplestore = (tuplestorestate == NULL) || |
86 | tuplestore_ateof(tuplestorestate); |
87 | |
88 | if (!forward && eof_tuplestore) |
89 | { |
90 | 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 | */ |
98 | if (!tuplestore_advance(tuplestorestate, forward)) |
99 | return NULL; /* the tuplestore must be empty */ |
100 | } |
101 | eof_tuplestore = false; |
102 | } |
103 | |
104 | /* |
105 | * If we can fetch another tuple from the tuplestore, return it. |
106 | */ |
107 | slot = node->ss.ps.ps_ResultTupleSlot; |
108 | if (!eof_tuplestore) |
109 | { |
110 | if (tuplestore_gettupleslot(tuplestorestate, forward, false, slot)) |
111 | return slot; |
112 | if (forward) |
113 | 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 | 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 | outerNode = outerPlanState(node); |
134 | outerslot = ExecProcNode(outerNode); |
135 | if (TupIsNull(outerslot)) |
136 | { |
137 | node->eof_underlying = true; |
138 | 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 | */ |
146 | if (tuplestorestate) |
147 | tuplestore_puttupleslot(tuplestorestate, outerslot); |
148 | |
149 | ExecCopySlot(slot, outerslot); |
150 | return slot; |
151 | } |
152 | |
153 | /* |
154 | * Nothing left ... |
155 | */ |
156 | return ExecClearTuple(slot); |
157 | } |
158 | |
159 | /* ---------------------------------------------------------------- |
160 | * ExecInitMaterial |
161 | * ---------------------------------------------------------------- |
162 | */ |
163 | MaterialState * |
164 | ExecInitMaterial(Material *node, EState *estate, int eflags) |
165 | { |
166 | MaterialState *matstate; |
167 | Plan *outerPlan; |
168 | |
169 | /* |
170 | * create state structure |
171 | */ |
172 | matstate = makeNode(MaterialState); |
173 | matstate->ss.ps.plan = (Plan *) node; |
174 | matstate->ss.ps.state = estate; |
175 | 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 | */ |
183 | 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 | */ |
194 | if (eflags & EXEC_FLAG_BACKWARD) |
195 | matstate->eflags |= EXEC_FLAG_REWIND; |
196 | |
197 | matstate->eof_underlying = false; |
198 | 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 | eflags &= ~(EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK); |
214 | |
215 | outerPlan = outerPlan(node); |
216 | 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 | */ |
224 | ExecInitResultTupleSlotTL(&matstate->ss.ps, &TTSOpsMinimalTuple); |
225 | matstate->ss.ps.ps_ProjInfo = NULL; |
226 | |
227 | /* |
228 | * initialize tuple type. |
229 | */ |
230 | ExecCreateScanSlotFromOuterPlan(estate, &matstate->ss, &TTSOpsMinimalTuple); |
231 | |
232 | return matstate; |
233 | } |
234 | |
235 | /* ---------------------------------------------------------------- |
236 | * ExecEndMaterial |
237 | * ---------------------------------------------------------------- |
238 | */ |
239 | void |
240 | ExecEndMaterial(MaterialState *node) |
241 | { |
242 | /* |
243 | * clean out the tuple table |
244 | */ |
245 | ExecClearTuple(node->ss.ss_ScanTupleSlot); |
246 | |
247 | /* |
248 | * Release tuplestore resources |
249 | */ |
250 | if (node->tuplestorestate != NULL) |
251 | tuplestore_end(node->tuplestorestate); |
252 | node->tuplestorestate = NULL; |
253 | |
254 | /* |
255 | * shut down the subplan |
256 | */ |
257 | ExecEndNode(outerPlanState(node)); |
258 | } |
259 | |
260 | /* ---------------------------------------------------------------- |
261 | * ExecMaterialMarkPos |
262 | * |
263 | * Calls tuplestore to save the current position in the stored file. |
264 | * ---------------------------------------------------------------- |
265 | */ |
266 | void |
267 | ExecMaterialMarkPos(MaterialState *node) |
268 | { |
269 | Assert(node->eflags & EXEC_FLAG_MARK); |
270 | |
271 | /* |
272 | * if we haven't materialized yet, just return. |
273 | */ |
274 | if (!node->tuplestorestate) |
275 | return; |
276 | |
277 | /* |
278 | * copy the active read pointer to the mark. |
279 | */ |
280 | tuplestore_copy_read_pointer(node->tuplestorestate, 0, 1); |
281 | |
282 | /* |
283 | * since we may have advanced the mark, try to truncate the tuplestore. |
284 | */ |
285 | tuplestore_trim(node->tuplestorestate); |
286 | } |
287 | |
288 | /* ---------------------------------------------------------------- |
289 | * ExecMaterialRestrPos |
290 | * |
291 | * Calls tuplestore to restore the last saved file position. |
292 | * ---------------------------------------------------------------- |
293 | */ |
294 | void |
295 | ExecMaterialRestrPos(MaterialState *node) |
296 | { |
297 | Assert(node->eflags & EXEC_FLAG_MARK); |
298 | |
299 | /* |
300 | * if we haven't materialized yet, just return. |
301 | */ |
302 | if (!node->tuplestorestate) |
303 | return; |
304 | |
305 | /* |
306 | * copy the mark to the active read pointer. |
307 | */ |
308 | tuplestore_copy_read_pointer(node->tuplestorestate, 1, 0); |
309 | } |
310 | |
311 | /* ---------------------------------------------------------------- |
312 | * ExecReScanMaterial |
313 | * |
314 | * Rescans the materialized relation. |
315 | * ---------------------------------------------------------------- |
316 | */ |
317 | void |
318 | ExecReScanMaterial(MaterialState *node) |
319 | { |
320 | PlanState *outerPlan = outerPlanState(node); |
321 | |
322 | ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); |
323 | |
324 | if (node->eflags != 0) |
325 | { |
326 | /* |
327 | * If we haven't materialized yet, just return. If outerplan's |
328 | * chgParam is not NULL then it will be re-scanned by ExecProcNode, |
329 | * else no reason to re-scan it at all. |
330 | */ |
331 | if (!node->tuplestorestate) |
332 | return; |
333 | |
334 | /* |
335 | * If subnode is to be rescanned then we forget previous stored |
336 | * results; we have to re-read the subplan and re-store. Also, if we |
337 | * told tuplestore it needn't support rescan, we lose and must |
338 | * re-read. (This last should not happen in common cases; else our |
339 | * caller lied by not passing EXEC_FLAG_REWIND to us.) |
340 | * |
341 | * Otherwise we can just rewind and rescan the stored output. The |
342 | * state of the subnode does not change. |
343 | */ |
344 | if (outerPlan->chgParam != NULL || |
345 | (node->eflags & EXEC_FLAG_REWIND) == 0) |
346 | { |
347 | tuplestore_end(node->tuplestorestate); |
348 | node->tuplestorestate = NULL; |
349 | if (outerPlan->chgParam == NULL) |
350 | ExecReScan(outerPlan); |
351 | node->eof_underlying = false; |
352 | } |
353 | else |
354 | tuplestore_rescan(node->tuplestorestate); |
355 | } |
356 | else |
357 | { |
358 | /* In this case we are just passing on the subquery's output */ |
359 | |
360 | /* |
361 | * if chgParam of subnode is not null then plan will be re-scanned by |
362 | * first ExecProcNode. |
363 | */ |
364 | if (outerPlan->chgParam == NULL) |
365 | ExecReScan(outerPlan); |
366 | node->eof_underlying = false; |
367 | } |
368 | } |
369 | |