88 *
99 *
1010 * IDENTIFICATION
11- * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.40 2002/12/15 16:17:46 tgl Exp $
11+ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.41 2003/03/09 02:19:13 tgl Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
2121 */
2222#include "postgres.h"
2323
24+ #include "access/heapam.h"
2425#include "executor/executor.h"
2526#include "executor/nodeMaterial.h"
2627#include "miscadmin.h"
2930/* ----------------------------------------------------------------
3031 * ExecMaterial
3132 *
32- * The first time this is called, ExecMaterial retrieves tuples
33- * from this node's outer subplan and inserts them into a tuplestore
34- * (a temporary tuple storage structure). The first tuple is then
35- * returned. Successive calls to ExecMaterial return successive
36- * tuples from the tuplestore.
37- *
38- * Initial State:
39- *
40- * matstate->tuplestorestate is initially NULL, indicating we
41- * haven't yet collected the results of the subplan.
33+ * As long as we are at the end of the data collected in the tuplestore,
34+ * we collect one new row from the subplan on each call, and stash it
35+ * aside in the tuplestore before returning it. The tuplestore is
36+ * only read if we are asked to scan backwards, rescan, or mark/restore.
4237 *
4338 * ----------------------------------------------------------------
4439 */
@@ -47,79 +42,106 @@ ExecMaterial(MaterialState *node)
4742{
4843 EState * estate ;
4944 ScanDirection dir ;
45+ bool forward ;
5046 Tuplestorestate * tuplestorestate ;
51- HeapTuple heapTuple ;
47+ HeapTuple heapTuple = NULL ;
48+ bool should_free = false;
49+ bool eof_tuplestore ;
5250 TupleTableSlot * slot ;
53- bool should_free ;
5451
5552 /*
5653 * get state info from node
5754 */
5855 estate = node -> ss .ps .state ;
5956 dir = estate -> es_direction ;
57+ forward = ScanDirectionIsForward (dir );
6058 tuplestorestate = (Tuplestorestate * ) node -> tuplestorestate ;
6159
6260 /*
63- * If first time through, read all tuples from outer plan and pass
64- * them to tuplestore.c. Subsequent calls just fetch tuples from
65- * tuplestore.
61+ * If first time through, initialize the tuplestore.
6662 */
67-
6863 if (tuplestorestate == NULL )
6964 {
70- PlanState * outerNode ;
71-
72- /*
73- * Want to scan subplan in the forward direction while creating
74- * the stored data. (Does setting my direction actually affect
75- * the subplan? I bet this is useless code...)
76- */
77- estate -> es_direction = ForwardScanDirection ;
78-
79- /*
80- * Initialize tuplestore module.
81- */
8265 tuplestorestate = tuplestore_begin_heap (true, /* randomAccess */
8366 SortMem );
8467
8568 node -> tuplestorestate = (void * ) tuplestorestate ;
69+ }
8670
87- /*
88- * Scan the subplan and feed all the tuples to tuplestore.
89- */
90- outerNode = outerPlanState (node );
71+ /*
72+ * If we are not at the end of the tuplestore, or are going backwards,
73+ * try to fetch a tuple from tuplestore.
74+ */
75+ eof_tuplestore = tuplestore_ateof (tuplestorestate );
9176
92- for (;;)
77+ if (!forward && eof_tuplestore )
78+ {
79+ if (!node -> eof_underlying )
9380 {
94- slot = ExecProcNode (outerNode );
81+ /*
82+ * When reversing direction at tuplestore EOF, the first
83+ * getheaptuple call will fetch the last-added tuple; but
84+ * we want to return the one before that, if possible.
85+ * So do an extra fetch.
86+ */
87+ heapTuple = tuplestore_getheaptuple (tuplestorestate ,
88+ forward ,
89+ & should_free );
90+ if (heapTuple == NULL )
91+ return NULL ; /* the tuplestore must be empty */
92+ if (should_free )
93+ heap_freetuple (heapTuple );
94+ }
95+ eof_tuplestore = false;
96+ }
9597
96- if (TupIsNull (slot ))
97- break ;
98+ if (!eof_tuplestore )
99+ {
100+ heapTuple = tuplestore_getheaptuple (tuplestorestate ,
101+ forward ,
102+ & should_free );
103+ if (heapTuple == NULL && forward )
104+ eof_tuplestore = true;
105+ }
98106
99- tuplestore_puttuple (tuplestorestate , (void * ) slot -> val );
100- ExecClearTuple (slot );
101- }
107+ /*
108+ * If necessary, try to fetch another row from the subplan.
109+ *
110+ * Note: the eof_underlying state variable exists to short-circuit
111+ * further subplan calls. It's not optional, unfortunately, because
112+ * some plan node types are not robust about being called again when
113+ * they've already returned NULL.
114+ */
115+ if (eof_tuplestore && !node -> eof_underlying )
116+ {
117+ PlanState * outerNode ;
118+ TupleTableSlot * outerslot ;
102119
103120 /*
104- * Complete the store.
121+ * We can only get here with forward==true, so no need to worry
122+ * about which direction the subplan will go.
105123 */
106- tuplestore_donestoring (tuplestorestate );
107-
124+ outerNode = outerPlanState (node );
125+ outerslot = ExecProcNode (outerNode );
126+ if (TupIsNull (outerslot ))
127+ {
128+ node -> eof_underlying = true;
129+ return NULL ;
130+ }
131+ heapTuple = outerslot -> val ;
132+ should_free = false;
108133 /*
109- * restore to user specified direction
134+ * Append returned tuple to tuplestore, too. NOTE: because the
135+ * tuplestore is certainly in EOF state, its read position will move
136+ * forward over the added tuple. This is what we want.
110137 */
111- estate -> es_direction = dir ;
138+ tuplestore_puttuple ( tuplestorestate , ( void * ) heapTuple ) ;
112139 }
113140
114141 /*
115- * Get the first or next tuple from tuplestore. Returns NULL if no
116- * more tuples.
142+ * Return the obtained tuple.
117143 */
118144 slot = (TupleTableSlot * ) node -> ss .ps .ps_ResultTupleSlot ;
119- heapTuple = tuplestore_getheaptuple (tuplestorestate ,
120- ScanDirectionIsForward (dir ),
121- & should_free );
122-
123145 return ExecStoreTuple (heapTuple , slot , InvalidBuffer , should_free );
124146}
125147
@@ -141,6 +163,7 @@ ExecInitMaterial(Material *node, EState *estate)
141163 matstate -> ss .ps .state = estate ;
142164
143165 matstate -> tuplestorestate = NULL ;
166+ matstate -> eof_underlying = false;
144167
145168 /*
146169 * Miscellaneous initialization
@@ -272,12 +295,16 @@ ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
272295 * results; we have to re-read the subplan and re-store.
273296 *
274297 * Otherwise we can just rewind and rescan the stored output.
298+ * The state of the subnode does not change.
275299 */
276300 if (((PlanState * ) node )-> lefttree -> chgParam != NULL )
277301 {
278302 tuplestore_end ((Tuplestorestate * ) node -> tuplestorestate );
279303 node -> tuplestorestate = NULL ;
304+ node -> eof_underlying = false;
280305 }
281306 else
307+ {
282308 tuplestore_rescan ((Tuplestorestate * ) node -> tuplestorestate );
309+ }
283310}
0 commit comments