Fix a many-legged critter reported by chifungfan@yahoo.com: under the
authorTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Aug 2000 04:06:22 +0000 (04:06 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Tue, 22 Aug 2000 04:06:22 +0000 (04:06 +0000)
right circumstances a hash join executed as a DECLARE CURSOR/FETCH
query would crash the backend.  Problem as seen in current sources was
that the hash tables were stored in a context that was a child of
TransactionCommandContext, which got zapped at completion of the FETCH
command --- but cursor cleanup executed at COMMIT expected the tables
to still be valid.  I haven't chased down the details as seen in 7.0.*
but I'm sure it's the same general problem.

src/backend/commands/copy.c
src/backend/executor/execMain.c
src/backend/executor/execUtils.c
src/backend/executor/nodeHash.c
src/backend/tcop/pquery.c
src/include/executor/hashjoin.h
src/include/nodes/execnodes.h

index c71d08180988da47f1d024fcd750cd674db380af..4b81a35c1228aa01c4b95b8be2738a15afd81840 100644 (file)
@@ -7,7 +7,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.120 2000/08/06 04:26:40 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.121 2000/08/22 04:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,6 +29,7 @@
 #include "executor/executor.h"
 #include "libpq/libpq.h"
 #include "miscadmin.h"
+#include "tcop/pquery.h"
 #include "tcop/tcopprot.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
@@ -598,7 +599,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
                tuples_read = 0;
    bool        reading_to_eof = true;
    RelationInfo *relationInfo;
-   EState     *estate = makeNode(EState);      /* for ExecConstraints() */
+   EState     *estate = CreateExecutorState(); /* for ExecConstraints() */
    TupleTable  tupleTable;
    TupleTableSlot *slot;
    Oid         loaded_oid = InvalidOid;
index e66107ce7d1e2150fbb0b86492f3627092f9e607..2db826144dc98e98e989f09a4d89e143db329061 100644 (file)
@@ -27,7 +27,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.123 2000/08/06 04:26:26 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.124 2000/08/22 04:06:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1574,31 +1574,32 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
 {
    int         ncheck = rel->rd_att->constr->num_check;
    ConstrCheck *check = rel->rd_att->constr->check;
-   MemoryContext oldContext;
    ExprContext *econtext;
+   MemoryContext oldContext;
    List       *qual;
    int         i;
 
    /*
-    * Make sure econtext, expressions, etc are placed in appropriate context.
+    * We will use the EState's per-tuple context for evaluating constraint
+    * expressions.  Create it if it's not already there; if it is, reset it
+    * to free previously-used storage.
     */
-   oldContext = MemoryContextSwitchTo(TransactionCommandContext);
-
-   /*
-    * Create or reset the exprcontext for evaluating constraint expressions.
-    */
-   econtext = estate->es_constraint_exprcontext;
+   econtext = estate->es_per_tuple_exprcontext;
    if (econtext == NULL)
-       estate->es_constraint_exprcontext = econtext =
-           MakeExprContext(NULL, TransactionCommandContext);
+   {
+       oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+       estate->es_per_tuple_exprcontext = econtext =
+           MakeExprContext(NULL, estate->es_query_cxt);
+       MemoryContextSwitchTo(oldContext);
+   }
    else
        ResetExprContext(econtext);
 
    /*
     * If first time through for current result relation, set up econtext's
     * range table to refer to result rel, and build expression nodetrees
-    * for rel's constraint expressions.  All this stuff is kept in
-    * TransactionCommandContext so it will still be here next time through.
+    * for rel's constraint expressions.  All this stuff is kept in the
+    * per-query memory context so it will still be here next time through.
     *
     * NOTE: if there are multiple result relations (eg, due to inheritance)
     * then we leak storage for prior rel's expressions and rangetable.
@@ -1608,7 +1609,14 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
    if (econtext->ecxt_range_table == NIL ||
        getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel))
    {
-       RangeTblEntry *rte = makeNode(RangeTblEntry);
+       RangeTblEntry *rte;
+
+       /*
+        * Make sure expressions, etc are placed in appropriate context.
+        */
+       oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+       rte = makeNode(RangeTblEntry);
 
        rte->relname = RelationGetRelationName(rel);
        rte->ref = makeNode(Attr);
@@ -1627,10 +1635,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
            qual = (List *) stringToNode(check[i].ccbin);
            estate->es_result_relation_constraints[i] = qual;
        }
-   }
 
-   /* Done with building long-lived items */
-   MemoryContextSwitchTo(oldContext);
+       /* Done with building long-lived items */
+       MemoryContextSwitchTo(oldContext);
+   }
 
    /* Arrange for econtext's scan tuple to be the tuple under test */
    econtext->ecxt_scantuple = slot;
index 39e3d5cd48bdbfd57c2b64e4c638c8d32ba3fc02..63c1e9e157f95e143a518c5676aae1a06781af55 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.64 2000/07/14 22:17:45 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.65 2000/08/22 04:06:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -866,9 +866,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
 
    heapTuple = slot->val;
 
-   /* ----------------
-    *  get information from the result relation info structure.
-    * ----------------
+   /*
+    * Get information from the result relation info structure.
     */
    resultRelationInfo = estate->es_result_relation_info;
    numIndices = resultRelationInfo->ri_NumIndices;
@@ -877,14 +876,26 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
    heapRelation = resultRelationInfo->ri_RelationDesc;
    heapDescriptor = RelationGetDescr(heapRelation);
 
-   /* ----------------
-    *  Make a temporary expr/memory context for evaluating predicates
-    *  and functional-index functions.
-    *  XXX should do this once per command not once per tuple, and
-    *  just reset it once per tuple.
-    * ----------------
+   /*
+    * We will use the EState's per-tuple context for evaluating predicates
+    * and functional-index functions.  Create it if it's not already there;
+    * if it is, reset it to free previously-used storage.
     */
-   econtext = MakeExprContext(slot, TransactionCommandContext);
+   econtext = estate->es_per_tuple_exprcontext;
+   if (econtext == NULL)
+   {
+       MemoryContext   oldContext;
+
+       oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+       estate->es_per_tuple_exprcontext = econtext =
+           MakeExprContext(NULL, estate->es_query_cxt);
+       MemoryContextSwitchTo(oldContext);
+   }
+   else
+       ResetExprContext(econtext);
+
+   /* Arrange for econtext's scan tuple to be the tuple under test */
+   econtext->ecxt_scantuple = slot;
 
    /* ----------------
     *  for each index, form and insert the index tuple
@@ -935,8 +946,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
        if (result)
            pfree(result);
    }
-
-   FreeExprContext(econtext);
 }
 
 void
index f63ffe4943575c812f601beaaf8abd00bd4aa204..682afdba4afa7e9620034af6501897a868175b44 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
- * $Id: nodeHash.c,v 1.50 2000/07/17 03:04:53 tgl Exp $
+ * $Id: nodeHash.c,v 1.51 2000/08/22 04:06:19 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -334,7 +334,8 @@ ExecHashTableCreate(Hash *node)
 
    /* ----------------
     *  Initialize the hash table control block.
-    *  The hashtable control block is just palloc'd from executor memory.
+    *  The hashtable control block is just palloc'd from the executor's
+    *  per-query memory context.
     * ----------------
     */
    hashtable = (HashJoinTable) palloc(sizeof(HashTableData));
@@ -361,7 +362,7 @@ ExecHashTableCreate(Hash *node)
     *  working storage.  See notes in executor/hashjoin.h.
     * ----------------
     */
-   hashtable->hashCxt = AllocSetContextCreate(TransactionCommandContext,
+   hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext,
                                               "HashTableContext",
                                               ALLOCSET_DEFAULT_MINSIZE,
                                               ALLOCSET_DEFAULT_INITSIZE,
index 104a82cde3e7f584980e04af59e034d9b6c2b39c..172f6fe467c7bf18e374cc7f637f789aee971e42 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.37 2000/07/17 03:05:15 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.38 2000/08/22 04:06:20 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,8 +68,8 @@ CreateExecutorState(void)
    state->es_direction = ForwardScanDirection;
    state->es_range_table = NIL;
 
-   state->es_into_relation_descriptor = NULL;
    state->es_result_relation_info = NULL;
+   state->es_into_relation_descriptor = NULL;
 
    state->es_param_list_info = NULL;
    state->es_param_exec_vals = NULL;
@@ -78,6 +78,10 @@ CreateExecutorState(void)
 
    state->es_junkFilter = NULL;
 
+   state->es_query_cxt = CurrentMemoryContext;
+
+   state->es_per_tuple_exprcontext = NULL;
+
    /* ----------------
     *  return the executor state structure
     * ----------------
@@ -144,13 +148,11 @@ PreparePortal(char *portalName)
    }
 
    /* ----------------
-    *   Create the new portal and make its memory context active.
+    *   Create the new portal.
     * ----------------
     */
    portal = CreatePortal(portalName);
 
-   MemoryContextSwitchTo(PortalGetHeapMemory(portal));
-
    return portal;
 }
 
@@ -170,8 +172,9 @@ ProcessQuery(Query *parsetree,
    char       *tag;
    bool        isRetrieveIntoPortal;
    bool        isRetrieveIntoRelation;
-   Portal      portal = NULL;
    char       *intoName = NULL;
+   Portal      portal = NULL;
+   MemoryContext oldContext = NULL;
    QueryDesc  *queryDesc;
    EState     *state;
    TupleDesc   attinfo;
@@ -217,14 +220,18 @@ ProcessQuery(Query *parsetree,
    if (isRetrieveIntoPortal)
    {
        portal = PreparePortal(intoName);
-       /* CurrentMemoryContext is now pointing to portal's context */
+       oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
        parsetree = copyObject(parsetree);
        plan = copyObject(plan);
+       /*
+        * We stay in portal's memory context for now, so that query desc,
+        * EState, and plan startup info are also allocated in the portal
+        * context.
+        */
    }
 
    /* ----------------
-    *  Now we can create the QueryDesc object (this is also in
-    *  the portal context, if portal retrieve).
+    *  Now we can create the QueryDesc object.
     * ----------------
     */
    queryDesc = CreateQueryDesc(parsetree, plan, dest);
@@ -241,7 +248,7 @@ ProcessQuery(Query *parsetree,
        queryDesc->dest = (int) None;
 
    /* ----------------
-    *  create a default executor state..
+    *  create a default executor state.
     * ----------------
     */
    state = CreateExecutorState();
@@ -279,9 +286,11 @@ ProcessQuery(Query *parsetree,
                       state,
                       PortalCleanup);
 
-       MemoryContextSwitchTo(TransactionCommandContext);
+       /* Now we can return to caller's memory context. */
+       MemoryContextSwitchTo(oldContext);
 
        EndCommand(tag, dest);
+
        return;
    }
 
index 8d4cb98469fd7336941cee618aa44810cb7d27c4..25a8174f294ec7173ef2d02a0a054b6d6b33aa93 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: hashjoin.h,v 1.18 2000/07/12 02:37:30 tgl Exp $
+ * $Id: hashjoin.h,v 1.19 2000/08/22 04:06:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,7 +26,7 @@
  * This makes it easy and fast to release the storage when we don't need it
  * anymore.
  *
- * The contexts are made children of TransactionCommandContext, ensuring
+ * The hashtable contexts are made children of the per-query context, ensuring
  * that they will be discarded at end of statement even if the join is
  * aborted early by an error.  (Likewise, any temporary files we make will
  * be cleaned up by the virtual file manager in event of an error.)
index 6e691c1d786c9f97e83a11b0aa18fd3fac6d605f..1ec14f4a9697d35d758ae4b25380b5af9cad950f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: execnodes.h,v 1.46 2000/08/13 02:50:24 tgl Exp $
+ * $Id: execnodes.h,v 1.47 2000/08/22 04:06:22 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -223,9 +223,14 @@ typedef struct EState
    uint32      es_processed;   /* # of tuples processed */
    Oid         es_lastoid;     /* last oid processed (by INSERT) */
    List       *es_rowMark;     /* not good place, but there is no other */
-   /* these two fields are storage space for ExecConstraints(): */
+   MemoryContext es_query_cxt; /* per-query context in which EState lives */
+   /* this ExprContext is for per-output-tuple operations, such as
+    * constraint checks and index-value computations.  It can be reset
+    * for each output tuple.  Note that it will be created only if needed.
+    */
+   ExprContext *es_per_tuple_exprcontext;
+   /* this field is storage space for ExecConstraints(): */
    List      **es_result_relation_constraints;
-   ExprContext *es_constraint_exprcontext;
    /* Below is to re-evaluate plan qual in READ COMMITTED mode */
    struct Plan *es_origPlan;
    Pointer     es_evalPlanQual;