mirror of
https://github.com/postgres/postgres.git
synced 2025-06-16 06:01:02 +03:00
Arrange for ValuesScan to keep per-sublist expression eval state in a
temporary context that can be reset when advancing to the next sublist. This is faster and more thorough at recovering space than the previous method; moreover it will do the right thing if something in the sublist tries to register an expression context callback.
This commit is contained in:
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.1 2006/08/02 01:59:45 joe Exp $
|
* $PostgreSQL: pgsql/src/backend/executor/nodeValuesscan.c,v 1.2 2006/08/02 18:58:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -30,9 +30,6 @@
|
|||||||
|
|
||||||
|
|
||||||
static TupleTableSlot *ValuesNext(ValuesScanState *node);
|
static TupleTableSlot *ValuesNext(ValuesScanState *node);
|
||||||
static void ExecMakeValuesResult(List *targetlist,
|
|
||||||
ExprContext *econtext,
|
|
||||||
TupleTableSlot *slot);
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
@ -61,7 +58,7 @@ ValuesNext(ValuesScanState *node)
|
|||||||
estate = node->ss.ps.state;
|
estate = node->ss.ps.state;
|
||||||
direction = estate->es_direction;
|
direction = estate->es_direction;
|
||||||
slot = node->ss.ss_ScanTupleSlot;
|
slot = node->ss.ss_ScanTupleSlot;
|
||||||
econtext = node->ss.ps.ps_ExprContext;
|
econtext = node->rowcontext;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Get the next tuple. Return NULL if no more tuples.
|
* Get the next tuple. Return NULL if no more tuples.
|
||||||
@ -85,57 +82,58 @@ ValuesNext(ValuesScanState *node)
|
|||||||
exprlist = NIL;
|
exprlist = NIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exprlist)
|
/*
|
||||||
{
|
* Always clear the result slot; this is appropriate if we are at the
|
||||||
List *init_exprlist;
|
* end of the data, and if we're not, we still need it as the first step
|
||||||
|
* of the store-virtual-tuple protocol. It seems wise to clear the slot
|
||||||
init_exprlist = (List *) ExecInitExpr((Expr *) exprlist,
|
* before we reset the context it might have pointers into.
|
||||||
(PlanState *) node);
|
*/
|
||||||
ExecMakeValuesResult(init_exprlist,
|
|
||||||
econtext,
|
|
||||||
slot);
|
|
||||||
list_free_deep(init_exprlist);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ExecClearTuple(slot);
|
ExecClearTuple(slot);
|
||||||
|
|
||||||
return slot;
|
if (exprlist)
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ExecMakeValuesResult
|
|
||||||
*
|
|
||||||
* Evaluate a values list, store into a virtual slot.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
ExecMakeValuesResult(List *targetlist,
|
|
||||||
ExprContext *econtext,
|
|
||||||
TupleTableSlot *slot)
|
|
||||||
{
|
{
|
||||||
MemoryContext oldContext;
|
MemoryContext oldContext;
|
||||||
|
List *exprstatelist;
|
||||||
Datum *values;
|
Datum *values;
|
||||||
bool *isnull;
|
bool *isnull;
|
||||||
ListCell *lc;
|
ListCell *lc;
|
||||||
int resind = 0;
|
int resind;
|
||||||
|
|
||||||
/* caller should have checked all targetlists are the same length */
|
|
||||||
Assert(list_length(targetlist) == slot->tts_tupleDescriptor->natts);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare to build a virtual result tuple.
|
* Get rid of any prior cycle's leftovers. We use ReScanExprContext
|
||||||
|
* not just ResetExprContext because we want any registered shutdown
|
||||||
|
* callbacks to be called.
|
||||||
|
*/
|
||||||
|
ReScanExprContext(econtext);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the expression eval state in the econtext's per-tuple
|
||||||
|
* memory. This is a tad unusual, but we want to delete the eval
|
||||||
|
* state again when we move to the next row, to avoid growth of
|
||||||
|
* memory requirements over a long values list.
|
||||||
|
*/
|
||||||
|
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass NULL, not my plan node, because we don't want anything
|
||||||
|
* in this transient state linking into permanent state. The
|
||||||
|
* only possibility is a SubPlan, and there shouldn't be any
|
||||||
|
* (any subselects in the VALUES list should be InitPlans).
|
||||||
|
*/
|
||||||
|
exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
|
||||||
|
|
||||||
|
/* parser should have checked all sublists are the same length */
|
||||||
|
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute the expressions and build a virtual result tuple.
|
||||||
|
* We already did ExecClearTuple(slot).
|
||||||
*/
|
*/
|
||||||
ExecClearTuple(slot);
|
|
||||||
values = slot->tts_values;
|
values = slot->tts_values;
|
||||||
isnull = slot->tts_isnull;
|
isnull = slot->tts_isnull;
|
||||||
|
|
||||||
/*
|
resind = 0;
|
||||||
* Switch to short-lived context for evaluating the row.
|
foreach(lc, exprstatelist)
|
||||||
* Reset per-tuple memory context before each row.
|
|
||||||
*/
|
|
||||||
ResetExprContext(econtext);
|
|
||||||
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
|
|
||||||
|
|
||||||
foreach(lc, targetlist)
|
|
||||||
{
|
{
|
||||||
ExprState *estate = (ExprState *) lfirst(lc);
|
ExprState *estate = (ExprState *) lfirst(lc);
|
||||||
|
|
||||||
@ -154,6 +152,9 @@ ExecMakeValuesResult(List *targetlist,
|
|||||||
ExecStoreVirtualTuple(slot);
|
ExecStoreVirtualTuple(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* ExecValuesScan(node)
|
* ExecValuesScan(node)
|
||||||
@ -186,7 +187,6 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
|
|||||||
ListCell *vtl;
|
ListCell *vtl;
|
||||||
int i;
|
int i;
|
||||||
PlanState *planstate;
|
PlanState *planstate;
|
||||||
ExprContext *econtext;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ValuesScan should not have any children.
|
* ValuesScan should not have any children.
|
||||||
@ -203,12 +203,17 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Miscellaneous initialization
|
* Miscellaneous initialization
|
||||||
*
|
|
||||||
* create expression context for node
|
|
||||||
*/
|
*/
|
||||||
planstate = &scanstate->ss.ps;
|
planstate = &scanstate->ss.ps;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create expression contexts. We need two, one for per-sublist
|
||||||
|
* processing and one for execScan.c to use for quals and projections.
|
||||||
|
* We cheat a little by using ExecAssignExprContext() to build both.
|
||||||
|
*/
|
||||||
|
ExecAssignExprContext(estate, planstate);
|
||||||
|
scanstate->rowcontext = planstate->ps_ExprContext;
|
||||||
ExecAssignExprContext(estate, planstate);
|
ExecAssignExprContext(estate, planstate);
|
||||||
econtext = planstate->ps_ExprContext;
|
|
||||||
|
|
||||||
#define VALUESSCAN_NSLOTS 2
|
#define VALUESSCAN_NSLOTS 2
|
||||||
|
|
||||||
@ -282,9 +287,11 @@ void
|
|||||||
ExecEndValuesScan(ValuesScanState *node)
|
ExecEndValuesScan(ValuesScanState *node)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Free the exprcontext
|
* Free both exprcontexts
|
||||||
*/
|
*/
|
||||||
ExecFreeExprContext(&node->ss.ps);
|
ExecFreeExprContext(&node->ss.ps);
|
||||||
|
node->ss.ps.ps_ExprContext = node->rowcontext;
|
||||||
|
ExecFreeExprContext(&node->ss.ps);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clean out the tuple table
|
* clean out the tuple table
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.156 2006/08/02 01:59:47 joe Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/execnodes.h,v 1.157 2006/08/02 18:58:21 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -1044,18 +1044,25 @@ typedef struct FunctionScanState
|
|||||||
/* ----------------
|
/* ----------------
|
||||||
* ValuesScanState information
|
* ValuesScanState information
|
||||||
*
|
*
|
||||||
* Values nodes are used to scan the results of a
|
* ValuesScan nodes are used to scan the results of a VALUES list
|
||||||
* values list appearing in FROM or INSERT
|
|
||||||
*
|
*
|
||||||
|
* rowcontext per-expression-list context
|
||||||
* exprlists array of expression lists being evaluated
|
* exprlists array of expression lists being evaluated
|
||||||
* array_len size of array
|
* array_len size of array
|
||||||
* curr_idx current array index (0-based)
|
* curr_idx current array index (0-based)
|
||||||
* marked_idx marked position (for mark/restore)
|
* marked_idx marked position (for mark/restore)
|
||||||
|
*
|
||||||
|
* Note: ss.ps.ps_ExprContext is used to evaluate any qual or projection
|
||||||
|
* expressions attached to the node. We create a second ExprContext,
|
||||||
|
* rowcontext, in which to build the executor expression state for each
|
||||||
|
* Values sublist. Resetting this context lets us get rid of expression
|
||||||
|
* state for each row, avoiding major memory leakage over a long values list.
|
||||||
* ----------------
|
* ----------------
|
||||||
*/
|
*/
|
||||||
typedef struct ValuesScanState
|
typedef struct ValuesScanState
|
||||||
{
|
{
|
||||||
ScanState ss; /* its first field is NodeTag */
|
ScanState ss; /* its first field is NodeTag */
|
||||||
|
ExprContext *rowcontext;
|
||||||
List **exprlists;
|
List **exprlists;
|
||||||
int array_len;
|
int array_len;
|
||||||
int curr_idx;
|
int curr_idx;
|
||||||
|
Reference in New Issue
Block a user