*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
static TupleTableSlot *ValuesNext(ValuesScanState *node);
-static void ExecMakeValuesResult(List *targetlist,
- ExprContext *econtext,
- TupleTableSlot *slot);
/* ----------------------------------------------------------------
estate = node->ss.ps.state;
direction = estate->es_direction;
slot = node->ss.ss_ScanTupleSlot;
- econtext = node->ss.ps.ps_ExprContext;
+ econtext = node->rowcontext;
/*
* Get the next tuple. Return NULL if no more tuples.
exprlist = NIL;
}
- if (exprlist)
- {
- List *init_exprlist;
-
- init_exprlist = (List *) ExecInitExpr((Expr *) exprlist,
- (PlanState *) node);
- ExecMakeValuesResult(init_exprlist,
- econtext,
- slot);
- list_free_deep(init_exprlist);
- }
- else
- ExecClearTuple(slot);
-
- return slot;
-}
-
-/*
- * ExecMakeValuesResult
- *
- * Evaluate a values list, store into a virtual slot.
- */
-static void
-ExecMakeValuesResult(List *targetlist,
- ExprContext *econtext,
- TupleTableSlot *slot)
-{
- MemoryContext oldContext;
- Datum *values;
- bool *isnull;
- ListCell *lc;
- int resind = 0;
-
- /* 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.
+ * Always clear the result slot; this is appropriate if we are at the
+ * 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
+ * before we reset the context it might have pointers into.
*/
ExecClearTuple(slot);
- values = slot->tts_values;
- isnull = slot->tts_isnull;
- /*
- * Switch to short-lived context for evaluating the row.
- * Reset per-tuple memory context before each row.
- */
- ResetExprContext(econtext);
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- foreach(lc, targetlist)
+ if (exprlist)
{
- ExprState *estate = (ExprState *) lfirst(lc);
-
- values[resind] = ExecEvalExpr(estate,
- econtext,
- &isnull[resind],
- NULL);
- resind++;
+ MemoryContext oldContext;
+ List *exprstatelist;
+ Datum *values;
+ bool *isnull;
+ ListCell *lc;
+ int resind;
+
+ /*
+ * 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).
+ */
+ values = slot->tts_values;
+ isnull = slot->tts_isnull;
+
+ resind = 0;
+ foreach(lc, exprstatelist)
+ {
+ ExprState *estate = (ExprState *) lfirst(lc);
+
+ values[resind] = ExecEvalExpr(estate,
+ econtext,
+ &isnull[resind],
+ NULL);
+ resind++;
+ }
+
+ MemoryContextSwitchTo(oldContext);
+
+ /*
+ * And return the virtual tuple.
+ */
+ ExecStoreVirtualTuple(slot);
}
- MemoryContextSwitchTo(oldContext);
-
- /*
- * And return the virtual tuple.
- */
- ExecStoreVirtualTuple(slot);
+ return slot;
}
ListCell *vtl;
int i;
PlanState *planstate;
- ExprContext *econtext;
/*
* ValuesScan should not have any children.
/*
* Miscellaneous initialization
- *
- * create expression context for node
*/
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);
- econtext = planstate->ps_ExprContext;
#define VALUESSCAN_NSLOTS 2
ExecEndValuesScan(ValuesScanState *node)
{
/*
- * Free the exprcontext
+ * Free both exprcontexts
*/
ExecFreeExprContext(&node->ss.ps);
+ node->ss.ps.ps_ExprContext = node->rowcontext;
+ ExecFreeExprContext(&node->ss.ps);
/*
* clean out the tuple table
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* 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 $
*
*-------------------------------------------------------------------------
*/
/* ----------------
* ValuesScanState information
*
- * Values nodes are used to scan the results of a
- * values list appearing in FROM or INSERT
+ * ValuesScan nodes are used to scan the results of a VALUES list
*
+ * rowcontext per-expression-list context
* exprlists array of expression lists being evaluated
* array_len size of array
* curr_idx current array index (0-based)
* 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
{
ScanState ss; /* its first field is NodeTag */
+ ExprContext *rowcontext;
List **exprlists;
int array_len;
int curr_idx;