Commit aa60eecc authored by Tom Lane's avatar Tom Lane

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.
parent 05a966fc
...@@ -743,10 +743,6 @@ connectby(char *relname, ...@@ -743,10 +743,6 @@ connectby(char *relname,
SPI_finish(); SPI_finish();
oldcontext = MemoryContextSwitchTo(per_query_ctx);
tuplestore_donestoring(tupstore);
MemoryContextSwitchTo(oldcontext);
return tupstore; return tupstore;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.125 2003/02/16 02:30:37 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.126 2003/03/09 02:19:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1107,13 +1107,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr, ...@@ -1107,13 +1107,6 @@ ExecMakeTableFunctionResult(ExprState *funcexpr,
first_time = false; first_time = false;
} }
/* If we have a locally-created tupstore, close it up */
if (tupstore)
{
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tuplestore_donestoring(tupstore);
}
MemoryContextSwitchTo(callerContext); MemoryContextSwitchTo(callerContext);
/* The returned pointers are those in rsinfo */ /* The returned pointers are those in rsinfo */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.40 2002/12/15 16:17:46 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.41 2003/03/09 02:19:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
*/ */
#include "postgres.h" #include "postgres.h"
#include "access/heapam.h"
#include "executor/executor.h" #include "executor/executor.h"
#include "executor/nodeMaterial.h" #include "executor/nodeMaterial.h"
#include "miscadmin.h" #include "miscadmin.h"
...@@ -29,16 +30,10 @@ ...@@ -29,16 +30,10 @@
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* ExecMaterial * ExecMaterial
* *
* The first time this is called, ExecMaterial retrieves tuples * As long as we are at the end of the data collected in the tuplestore,
* from this node's outer subplan and inserts them into a tuplestore * we collect one new row from the subplan on each call, and stash it
* (a temporary tuple storage structure). The first tuple is then * aside in the tuplestore before returning it. The tuplestore is
* returned. Successive calls to ExecMaterial return successive * only read if we are asked to scan backwards, rescan, or mark/restore.
* tuples from the tuplestore.
*
* Initial State:
*
* matstate->tuplestorestate is initially NULL, indicating we
* haven't yet collected the results of the subplan.
* *
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
...@@ -47,79 +42,106 @@ ExecMaterial(MaterialState *node) ...@@ -47,79 +42,106 @@ ExecMaterial(MaterialState *node)
{ {
EState *estate; EState *estate;
ScanDirection dir; ScanDirection dir;
bool forward;
Tuplestorestate *tuplestorestate; Tuplestorestate *tuplestorestate;
HeapTuple heapTuple; HeapTuple heapTuple = NULL;
bool should_free = false;
bool eof_tuplestore;
TupleTableSlot *slot; TupleTableSlot *slot;
bool should_free;
/* /*
* get state info from node * get state info from node
*/ */
estate = node->ss.ps.state; estate = node->ss.ps.state;
dir = estate->es_direction; dir = estate->es_direction;
forward = ScanDirectionIsForward(dir);
tuplestorestate = (Tuplestorestate *) node->tuplestorestate; tuplestorestate = (Tuplestorestate *) node->tuplestorestate;
/* /*
* If first time through, read all tuples from outer plan and pass * If first time through, initialize the tuplestore.
* them to tuplestore.c. Subsequent calls just fetch tuples from
* tuplestore.
*/ */
if (tuplestorestate == NULL) if (tuplestorestate == NULL)
{ {
PlanState *outerNode;
/*
* Want to scan subplan in the forward direction while creating
* the stored data. (Does setting my direction actually affect
* the subplan? I bet this is useless code...)
*/
estate->es_direction = ForwardScanDirection;
/*
* Initialize tuplestore module.
*/
tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */ tuplestorestate = tuplestore_begin_heap(true, /* randomAccess */
SortMem); SortMem);
node->tuplestorestate = (void *) tuplestorestate; node->tuplestorestate = (void *) tuplestorestate;
}
/* /*
* Scan the subplan and feed all the tuples to tuplestore. * If we are not at the end of the tuplestore, or are going backwards,
*/ * try to fetch a tuple from tuplestore.
outerNode = outerPlanState(node); */
eof_tuplestore = tuplestore_ateof(tuplestorestate);
for (;;) if (!forward && eof_tuplestore)
{
if (!node->eof_underlying)
{ {
slot = ExecProcNode(outerNode); /*
* When reversing direction at tuplestore EOF, the first
* getheaptuple call will fetch the last-added tuple; but
* we want to return the one before that, if possible.
* So do an extra fetch.
*/
heapTuple = tuplestore_getheaptuple(tuplestorestate,
forward,
&should_free);
if (heapTuple == NULL)
return NULL; /* the tuplestore must be empty */
if (should_free)
heap_freetuple(heapTuple);
}
eof_tuplestore = false;
}
if (TupIsNull(slot)) if (!eof_tuplestore)
break; {
heapTuple = tuplestore_getheaptuple(tuplestorestate,
forward,
&should_free);
if (heapTuple == NULL && forward)
eof_tuplestore = true;
}
tuplestore_puttuple(tuplestorestate, (void *) slot->val); /*
ExecClearTuple(slot); * If necessary, try to fetch another row from the subplan.
} *
* Note: the eof_underlying state variable exists to short-circuit
* further subplan calls. It's not optional, unfortunately, because
* some plan node types are not robust about being called again when
* they've already returned NULL.
*/
if (eof_tuplestore && !node->eof_underlying)
{
PlanState *outerNode;
TupleTableSlot *outerslot;
/* /*
* Complete the store. * We can only get here with forward==true, so no need to worry
* about which direction the subplan will go.
*/ */
tuplestore_donestoring(tuplestorestate); outerNode = outerPlanState(node);
outerslot = ExecProcNode(outerNode);
if (TupIsNull(outerslot))
{
node->eof_underlying = true;
return NULL;
}
heapTuple = outerslot->val;
should_free = false;
/* /*
* restore to user specified direction * Append returned tuple to tuplestore, too. NOTE: because the
* tuplestore is certainly in EOF state, its read position will move
* forward over the added tuple. This is what we want.
*/ */
estate->es_direction = dir; tuplestore_puttuple(tuplestorestate, (void *) heapTuple);
} }
/* /*
* Get the first or next tuple from tuplestore. Returns NULL if no * Return the obtained tuple.
* more tuples.
*/ */
slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot; slot = (TupleTableSlot *) node->ss.ps.ps_ResultTupleSlot;
heapTuple = tuplestore_getheaptuple(tuplestorestate,
ScanDirectionIsForward(dir),
&should_free);
return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free); return ExecStoreTuple(heapTuple, slot, InvalidBuffer, should_free);
} }
...@@ -141,6 +163,7 @@ ExecInitMaterial(Material *node, EState *estate) ...@@ -141,6 +163,7 @@ ExecInitMaterial(Material *node, EState *estate)
matstate->ss.ps.state = estate; matstate->ss.ps.state = estate;
matstate->tuplestorestate = NULL; matstate->tuplestorestate = NULL;
matstate->eof_underlying = false;
/* /*
* Miscellaneous initialization * Miscellaneous initialization
...@@ -272,12 +295,16 @@ ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt) ...@@ -272,12 +295,16 @@ ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt)
* results; we have to re-read the subplan and re-store. * results; we have to re-read the subplan and re-store.
* *
* Otherwise we can just rewind and rescan the stored output. * Otherwise we can just rewind and rescan the stored output.
* The state of the subnode does not change.
*/ */
if (((PlanState *) node)->lefttree->chgParam != NULL) if (((PlanState *) node)->lefttree->chgParam != NULL)
{ {
tuplestore_end((Tuplestorestate *) node->tuplestorestate); tuplestore_end((Tuplestorestate *) node->tuplestorestate);
node->tuplestorestate = NULL; node->tuplestorestate = NULL;
node->eof_underlying = false;
} }
else else
{
tuplestore_rescan((Tuplestorestate *) node->tuplestorestate); tuplestore_rescan((Tuplestorestate *) node->tuplestorestate);
}
} }
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: execnodes.h,v 1.95 2003/02/16 02:30:39 tgl Exp $ * $Id: execnodes.h,v 1.96 2003/03/09 02:19:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -917,14 +917,13 @@ typedef struct HashJoinState ...@@ -917,14 +917,13 @@ typedef struct HashJoinState
* of a subplan into a temporary file. * of a subplan into a temporary file.
* *
* ss.ss_ScanTupleSlot refers to output of underlying plan. * ss.ss_ScanTupleSlot refers to output of underlying plan.
*
* tuplestorestate private state of tuplestore.c
* ---------------- * ----------------
*/ */
typedef struct MaterialState typedef struct MaterialState
{ {
ScanState ss; /* its first field is NodeTag */ ScanState ss; /* its first field is NodeTag */
void *tuplestorestate; void *tuplestorestate; /* private state of tuplestore.c */
bool eof_underlying; /* reached end of underlying plan? */
} MaterialState; } MaterialState;
/* ---------------- /* ----------------
......
...@@ -6,14 +6,18 @@ ...@@ -6,14 +6,18 @@
* This module handles temporary storage of tuples for purposes such * This module handles temporary storage of tuples for purposes such
* as Materialize nodes, hashjoin batch files, etc. It is essentially * as Materialize nodes, hashjoin batch files, etc. It is essentially
* a dumbed-down version of tuplesort.c; it does no sorting of tuples * a dumbed-down version of tuplesort.c; it does no sorting of tuples
* but can only store a sequence of tuples and regurgitate it later. * but can only store and regurgitate a sequence of tuples. However,
* because no sort is required, it is allowed to start reading the sequence
* before it has all been written. This is particularly useful for cursors,
* because it allows random access within the already-scanned portion of
* a query without having to process the underlying scan to completion.
* A temporary file is used to handle the data if it exceeds the * A temporary file is used to handle the data if it exceeds the
* space limit specified by the caller. * space limit specified by the caller.
* *
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: tuplestore.h,v 1.7 2002/06/20 20:29:53 momjian Exp $ * $Id: tuplestore.h,v 1.8 2003/03/09 02:19:13 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -37,8 +41,7 @@ extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess, ...@@ -37,8 +41,7 @@ extern Tuplestorestate *tuplestore_begin_heap(bool randomAccess,
extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple); extern void tuplestore_puttuple(Tuplestorestate *state, void *tuple);
extern void tuplestore_donestoring(Tuplestorestate *state); /* backwards scan is only allowed if randomAccess was specified 'true' */
extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward, extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward,
bool *should_free); bool *should_free);
...@@ -47,11 +50,7 @@ extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward, ...@@ -47,11 +50,7 @@ extern void *tuplestore_gettuple(Tuplestorestate *state, bool forward,
extern void tuplestore_end(Tuplestorestate *state); extern void tuplestore_end(Tuplestorestate *state);
/* extern bool tuplestore_ateof(Tuplestorestate *state);
* These routines may only be called if randomAccess was specified 'true'.
* Likewise, backwards scan in gettuple/getdatum is only allowed if
* randomAccess was specified.
*/
extern void tuplestore_rescan(Tuplestorestate *state); extern void tuplestore_rescan(Tuplestorestate *state);
extern void tuplestore_markpos(Tuplestorestate *state); extern void tuplestore_markpos(Tuplestorestate *state);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* procedural language * procedural language
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.80 2003/03/02 20:45:47 tgl Exp $ * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.81 2003/03/09 02:19:13 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -348,14 +348,15 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo) ...@@ -348,14 +348,15 @@ plpgsql_exec_function(PLpgSQL_function * func, FunctionCallInfo fcinfo)
/* If we produced any tuples, send back the result */ /* If we produced any tuples, send back the result */
if (estate.tuple_store) if (estate.tuple_store)
{ {
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
tuplestore_donestoring(estate.tuple_store);
rsi->setResult = estate.tuple_store; rsi->setResult = estate.tuple_store;
if (estate.rettupdesc) if (estate.rettupdesc)
{
MemoryContext oldcxt;
oldcxt = MemoryContextSwitchTo(estate.tuple_store_cxt);
rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc); rsi->setDesc = CreateTupleDescCopy(estate.rettupdesc);
MemoryContextSwitchTo(oldcxt); MemoryContextSwitchTo(oldcxt);
}
} }
estate.retval = (Datum) 0; estate.retval = (Datum) 0;
fcinfo->isnull = true; fcinfo->isnull = true;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment