Arrange for ValuesScan to keep per-sublist expression eval state in a
authorTom Lane
Wed, 2 Aug 2006 18:58:21 +0000 (18:58 +0000)
committerTom Lane
Wed, 2 Aug 2006 18:58:21 +0000 (18:58 +0000)
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.

src/backend/executor/nodeValuesscan.c
src/include/nodes/execnodes.h

index eb053d8cc76db849503c00d9bcfee7a41f272128..1d4bb08d4dded3fe28119d37d722c7914323c045 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * 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 void ExecMakeValuesResult(List *targetlist,
-                    ExprContext *econtext,
-                    TupleTableSlot *slot);
 
 
 /* ----------------------------------------------------------------
@@ -61,7 +58,7 @@ ValuesNext(ValuesScanState *node)
    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.
@@ -85,73 +82,77 @@ ValuesNext(ValuesScanState *node)
            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;
 }
 
 
@@ -186,7 +187,6 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
    ListCell           *vtl;
    int                 i;
    PlanState          *planstate;
-   ExprContext        *econtext;
 
    /*
     * ValuesScan should not have any children.
@@ -203,12 +203,17 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
 
    /*
     * 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
 
@@ -282,9 +287,11 @@ void
 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
index 2e98124003705e7e31e28ed614192bc1e098cbf8..fbf70f5152622530f5cd5fb5a43a186aed320b54 100644 (file)
@@ -7,7 +7,7 @@
  * 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 $
  *
  *-------------------------------------------------------------------------
  */
@@ -1044,18 +1044,25 @@ typedef struct FunctionScanState
 /* ----------------
  *  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;