1 /*-------------------------------------------------------------------------
4 * Routines to handle materialization nodes.
6 * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * src/backend/executor/nodeMaterial.c
13 *-------------------------------------------------------------------------
17 * ExecMaterial - materialize the result of a subplan
18 * ExecInitMaterial - initialize node and subnodes
19 * ExecEndMaterial - shutdown node and subnodes
24 #include "executor/executor.h"
25 #include "executor/nodeMaterial.h"
26 #include "miscadmin.h"
28 /* ----------------------------------------------------------------
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.
36 * ----------------------------------------------------------------
38 static TupleTableSlot
* /* result tuple from subplan */
39 ExecMaterial(PlanState
*pstate
)
41 MaterialState
*node
= castNode(MaterialState
, pstate
);
45 Tuplestorestate
*tuplestorestate
;
49 CHECK_FOR_INTERRUPTS();
52 * get state info from node
54 estate
= node
->ss
.ps
.state
;
55 dir
= estate
->es_direction
;
56 forward
= ScanDirectionIsForward(dir
);
57 tuplestorestate
= node
->tuplestorestate
;
60 * If first time through, and we need a tuplestore, initialize it.
62 if (tuplestorestate
== NULL
&& node
->eflags
!= 0)
64 tuplestorestate
= tuplestore_begin_heap(true, false, work_mem
);
65 tuplestore_set_eflags(tuplestorestate
, node
->eflags
);
66 if (node
->eflags
& EXEC_FLAG_MARK
)
69 * Allocate a second read pointer to serve as the mark. We know it
70 * must have index 1, so needn't store that.
72 int ptrno PG_USED_FOR_ASSERTS_ONLY
;
74 ptrno
= tuplestore_alloc_read_pointer(tuplestorestate
,
78 node
->tuplestorestate
= tuplestorestate
;
82 * If we are not at the end of the tuplestore, or are going backwards, try
83 * to fetch a tuple from tuplestore.
85 eof_tuplestore
= (tuplestorestate
== NULL
) ||
86 tuplestore_ateof(tuplestorestate
);
88 if (!forward
&& eof_tuplestore
)
90 if (!node
->eof_underlying
)
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
98 if (!tuplestore_advance(tuplestorestate
, forward
))
99 return NULL
; /* the tuplestore must be empty */
101 eof_tuplestore
= false;
105 * If we can fetch another tuple from the tuplestore, return it.
107 slot
= node
->ss
.ps
.ps_ResultTupleSlot
;
110 if (tuplestore_gettupleslot(tuplestorestate
, forward
, false, slot
))
113 eof_tuplestore
= true;
117 * If necessary, try to fetch another row from the subplan.
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
124 if (eof_tuplestore
&& !node
->eof_underlying
)
126 PlanState
*outerNode
;
127 TupleTableSlot
*outerslot
;
130 * We can only get here with forward==true, so no need to worry about
131 * which direction the subplan will go.
133 outerNode
= outerPlanState(node
);
134 outerslot
= ExecProcNode(outerNode
);
135 if (TupIsNull(outerslot
))
137 node
->eof_underlying
= true;
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.
147 tuplestore_puttupleslot(tuplestorestate
, outerslot
);
149 ExecCopySlot(slot
, outerslot
);
156 return ExecClearTuple(slot
);
159 /* ----------------------------------------------------------------
161 * ----------------------------------------------------------------
164 ExecInitMaterial(Material
*node
, EState
*estate
, int eflags
)
166 MaterialState
*matstate
;
170 * create state structure
172 matstate
= makeNode(MaterialState
);
173 matstate
->ss
.ps
.plan
= (Plan
*) node
;
174 matstate
->ss
.ps
.state
= estate
;
175 matstate
->ss
.ps
.ExecProcNode
= ExecMaterial
;
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.
183 matstate
->eflags
= (eflags
& (EXEC_FLAG_REWIND
|
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.
194 if (eflags
& EXEC_FLAG_BACKWARD
)
195 matstate
->eflags
|= EXEC_FLAG_REWIND
;
197 matstate
->eof_underlying
= false;
198 matstate
->tuplestorestate
= NULL
;
201 * Miscellaneous initialization
203 * Materialization nodes don't need ExprContexts because they never call
204 * ExecQual or ExecProject.
208 * initialize child nodes
210 * We shield the child node from the need to support REWIND, BACKWARD, or
213 eflags
&= ~(EXEC_FLAG_REWIND
| EXEC_FLAG_BACKWARD
| EXEC_FLAG_MARK
);
215 outerPlan
= outerPlan(node
);
216 outerPlanState(matstate
) = ExecInitNode(outerPlan
, estate
, eflags
);
219 * Initialize result type and slot. No need to initialize projection info
220 * because this node doesn't do projections.
222 * material nodes only return tuples from their materialized relation.
224 ExecInitResultTupleSlotTL(&matstate
->ss
.ps
, &TTSOpsMinimalTuple
);
225 matstate
->ss
.ps
.ps_ProjInfo
= NULL
;
228 * initialize tuple type.
230 ExecCreateScanSlotFromOuterPlan(estate
, &matstate
->ss
, &TTSOpsMinimalTuple
);
235 /* ----------------------------------------------------------------
237 * ----------------------------------------------------------------
240 ExecEndMaterial(MaterialState
*node
)
243 * Release tuplestore resources
245 if (node
->tuplestorestate
!= NULL
)
246 tuplestore_end(node
->tuplestorestate
);
247 node
->tuplestorestate
= NULL
;
250 * shut down the subplan
252 ExecEndNode(outerPlanState(node
));
255 /* ----------------------------------------------------------------
256 * ExecMaterialMarkPos
258 * Calls tuplestore to save the current position in the stored file.
259 * ----------------------------------------------------------------
262 ExecMaterialMarkPos(MaterialState
*node
)
264 Assert(node
->eflags
& EXEC_FLAG_MARK
);
267 * if we haven't materialized yet, just return.
269 if (!node
->tuplestorestate
)
273 * copy the active read pointer to the mark.
275 tuplestore_copy_read_pointer(node
->tuplestorestate
, 0, 1);
278 * since we may have advanced the mark, try to truncate the tuplestore.
280 tuplestore_trim(node
->tuplestorestate
);
283 /* ----------------------------------------------------------------
284 * ExecMaterialRestrPos
286 * Calls tuplestore to restore the last saved file position.
287 * ----------------------------------------------------------------
290 ExecMaterialRestrPos(MaterialState
*node
)
292 Assert(node
->eflags
& EXEC_FLAG_MARK
);
295 * if we haven't materialized yet, just return.
297 if (!node
->tuplestorestate
)
301 * copy the mark to the active read pointer.
303 tuplestore_copy_read_pointer(node
->tuplestorestate
, 1, 0);
306 /* ----------------------------------------------------------------
309 * Rescans the materialized relation.
310 * ----------------------------------------------------------------
313 ExecReScanMaterial(MaterialState
*node
)
315 PlanState
*outerPlan
= outerPlanState(node
);
317 ExecClearTuple(node
->ss
.ps
.ps_ResultTupleSlot
);
319 if (node
->eflags
!= 0)
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.
326 if (!node
->tuplestorestate
)
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.)
336 * Otherwise we can just rewind and rescan the stored output. The
337 * state of the subnode does not change.
339 if (outerPlan
->chgParam
!= NULL
||
340 (node
->eflags
& EXEC_FLAG_REWIND
) == 0)
342 tuplestore_end(node
->tuplestorestate
);
343 node
->tuplestorestate
= NULL
;
344 if (outerPlan
->chgParam
== NULL
)
345 ExecReScan(outerPlan
);
346 node
->eof_underlying
= false;
349 tuplestore_rescan(node
->tuplestorestate
);
353 /* In this case we are just passing on the subquery's output */
356 * if chgParam of subnode is not null then plan will be re-scanned by
357 * first ExecProcNode.
359 if (outerPlan
->chgParam
== NULL
)
360 ExecReScan(outerPlan
);
361 node
->eof_underlying
= false;