Skip to content

Commit aa60eec

Browse files
committed
Revise tuplestore and nodeMaterial so that we don't have to read the
entire contents of the subplan into the tuplestore before we can return any tuples. Instead, the tuplestore holds what we've already read, and we fetch additional rows from the subplan as needed. Random access to the previously-read rows works with the tuplestore, and doesn't affect the state of the partially-read subplan. This is a step towards fixing the problems with cursors over complex queries --- we don't want to stick in Materialize nodes if they'll prevent quick startup for a cursor.
1 parent 05a966f commit aa60eec

File tree

7 files changed

+267
-227
lines changed

7 files changed

+267
-227
lines changed

contrib/tablefunc/tablefunc.c

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -743,10 +743,6 @@ connectby(char *relname,
743743

744744
SPI_finish();
745745

746-
oldcontext = MemoryContextSwitchTo(per_query_ctx);
747-
tuplestore_donestoring(tupstore);
748-
MemoryContextSwitchTo(oldcontext);
749-
750746
return tupstore;
751747
}
752748

src/backend/executor/execQual.c

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
*
99
*
1010
* IDENTIFICATION
11-
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.125 2003/02/16 02:30:37 tgl Exp $
11+
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.126 2003/03/09 02:19:13 tgl Exp $
1212
*
1313
*-------------------------------------------------------------------------
1414
*/
@@ -1107,13 +1107,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
11071107
first_time = false;
11081108
}
11091109

1110-
/* If we have a locally-created tupstore, close it up */
1111-
if (tupstore)
1112-
{
1113-
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
1114-
tuplestore_donestoring(tupstore);
1115-
}
1116-
11171110
MemoryContextSwitchTo(callerContext);
11181111

11191112
/* The returned pointers are those in rsinfo */

src/backend/executor/nodeMaterial.c

Lines changed: 78 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
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
*/
@@ -21,6 +21,7 @@
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"
@@ -29,16 +30,10 @@
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

Comments
 (0)