Commit 0147b193 authored by Tom Lane's avatar Tom Lane

Fix a many-legged critter reported by chifungfan@yahoo.com: under the

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.
parent 94e90d9a
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -29,6 +29,7 @@
#include "executor/executor.h" #include "executor/executor.h"
#include "libpq/libpq.h" #include "libpq/libpq.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "tcop/pquery.h"
#include "tcop/tcopprot.h" #include "tcop/tcopprot.h"
#include "utils/acl.h" #include "utils/acl.h"
#include "utils/builtins.h" #include "utils/builtins.h"
...@@ -598,7 +599,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp, ...@@ -598,7 +599,7 @@ CopyFrom(Relation rel, bool binary, bool oids, FILE *fp,
tuples_read = 0; tuples_read = 0;
bool reading_to_eof = true; bool reading_to_eof = true;
RelationInfo *relationInfo; RelationInfo *relationInfo;
EState *estate = makeNode(EState); /* for ExecConstraints() */ EState *estate = CreateExecutorState(); /* for ExecConstraints() */
TupleTable tupleTable; TupleTable tupleTable;
TupleTableSlot *slot; TupleTableSlot *slot;
Oid loaded_oid = InvalidOid; Oid loaded_oid = InvalidOid;
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -1574,31 +1574,32 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
{ {
int ncheck = rel->rd_att->constr->num_check; int ncheck = rel->rd_att->constr->num_check;
ConstrCheck *check = rel->rd_att->constr->check; ConstrCheck *check = rel->rd_att->constr->check;
MemoryContext oldContext;
ExprContext *econtext; ExprContext *econtext;
MemoryContext oldContext;
List *qual; List *qual;
int i; 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); econtext = estate->es_per_tuple_exprcontext;
/*
* Create or reset the exprcontext for evaluating constraint expressions.
*/
econtext = estate->es_constraint_exprcontext;
if (econtext == NULL) 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 else
ResetExprContext(econtext); ResetExprContext(econtext);
/* /*
* If first time through for current result relation, set up econtext's * If first time through for current result relation, set up econtext's
* range table to refer to result rel, and build expression nodetrees * range table to refer to result rel, and build expression nodetrees
* for rel's constraint expressions. All this stuff is kept in * for rel's constraint expressions. All this stuff is kept in the
* TransactionCommandContext so it will still be here next time through. * per-query memory context so it will still be here next time through.
* *
* NOTE: if there are multiple result relations (eg, due to inheritance) * NOTE: if there are multiple result relations (eg, due to inheritance)
* then we leak storage for prior rel's expressions and rangetable. * then we leak storage for prior rel's expressions and rangetable.
...@@ -1608,7 +1609,14 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1608,7 +1609,14 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
if (econtext->ecxt_range_table == NIL || if (econtext->ecxt_range_table == NIL ||
getrelid(1, econtext->ecxt_range_table) != RelationGetRelid(rel)) 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->relname = RelationGetRelationName(rel);
rte->ref = makeNode(Attr); rte->ref = makeNode(Attr);
...@@ -1627,10 +1635,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate) ...@@ -1627,10 +1635,10 @@ ExecRelCheck(Relation rel, TupleTableSlot *slot, EState *estate)
qual = (List *) stringToNode(check[i].ccbin); qual = (List *) stringToNode(check[i].ccbin);
estate->es_result_relation_constraints[i] = qual; estate->es_result_relation_constraints[i] = qual;
} }
}
/* Done with building long-lived items */ /* Done with building long-lived items */
MemoryContextSwitchTo(oldContext); MemoryContextSwitchTo(oldContext);
}
/* Arrange for econtext's scan tuple to be the tuple under test */ /* Arrange for econtext's scan tuple to be the tuple under test */
econtext->ecxt_scantuple = slot; econtext->ecxt_scantuple = slot;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -866,9 +866,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
heapTuple = slot->val; 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; resultRelationInfo = estate->es_result_relation_info;
numIndices = resultRelationInfo->ri_NumIndices; numIndices = resultRelationInfo->ri_NumIndices;
...@@ -877,14 +876,26 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -877,14 +876,26 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
heapRelation = resultRelationInfo->ri_RelationDesc; heapRelation = resultRelationInfo->ri_RelationDesc;
heapDescriptor = RelationGetDescr(heapRelation); heapDescriptor = RelationGetDescr(heapRelation);
/* ---------------- /*
* Make a temporary expr/memory context for evaluating predicates * We will use the EState's per-tuple context for evaluating predicates
* and functional-index functions. * and functional-index functions. Create it if it's not already there;
* XXX should do this once per command not once per tuple, and * if it is, reset it to free previously-used storage.
* just reset it once per tuple.
* ----------------
*/ */
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 * for each index, form and insert the index tuple
...@@ -935,8 +946,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot, ...@@ -935,8 +946,6 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
if (result) if (result)
pfree(result); pfree(result);
} }
FreeExprContext(econtext);
} }
void void
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * 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) ...@@ -334,7 +334,8 @@ ExecHashTableCreate(Hash *node)
/* ---------------- /* ----------------
* Initialize the hash table control block. * 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)); hashtable = (HashJoinTable) palloc(sizeof(HashTableData));
...@@ -361,7 +362,7 @@ ExecHashTableCreate(Hash *node) ...@@ -361,7 +362,7 @@ ExecHashTableCreate(Hash *node)
* working storage. See notes in executor/hashjoin.h. * working storage. See notes in executor/hashjoin.h.
* ---------------- * ----------------
*/ */
hashtable->hashCxt = AllocSetContextCreate(TransactionCommandContext, hashtable->hashCxt = AllocSetContextCreate(CurrentMemoryContext,
"HashTableContext", "HashTableContext",
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_INITSIZE,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -68,8 +68,8 @@ CreateExecutorState(void)
state->es_direction = ForwardScanDirection; state->es_direction = ForwardScanDirection;
state->es_range_table = NIL; state->es_range_table = NIL;
state->es_into_relation_descriptor = NULL;
state->es_result_relation_info = NULL; state->es_result_relation_info = NULL;
state->es_into_relation_descriptor = NULL;
state->es_param_list_info = NULL; state->es_param_list_info = NULL;
state->es_param_exec_vals = NULL; state->es_param_exec_vals = NULL;
...@@ -78,6 +78,10 @@ CreateExecutorState(void) ...@@ -78,6 +78,10 @@ CreateExecutorState(void)
state->es_junkFilter = NULL; state->es_junkFilter = NULL;
state->es_query_cxt = CurrentMemoryContext;
state->es_per_tuple_exprcontext = NULL;
/* ---------------- /* ----------------
* return the executor state structure * return the executor state structure
* ---------------- * ----------------
...@@ -144,13 +148,11 @@ PreparePortal(char *portalName) ...@@ -144,13 +148,11 @@ PreparePortal(char *portalName)
} }
/* ---------------- /* ----------------
* Create the new portal and make its memory context active. * Create the new portal.
* ---------------- * ----------------
*/ */
portal = CreatePortal(portalName); portal = CreatePortal(portalName);
MemoryContextSwitchTo(PortalGetHeapMemory(portal));
return portal; return portal;
} }
...@@ -170,8 +172,9 @@ ProcessQuery(Query *parsetree, ...@@ -170,8 +172,9 @@ ProcessQuery(Query *parsetree,
char *tag; char *tag;
bool isRetrieveIntoPortal; bool isRetrieveIntoPortal;
bool isRetrieveIntoRelation; bool isRetrieveIntoRelation;
Portal portal = NULL;
char *intoName = NULL; char *intoName = NULL;
Portal portal = NULL;
MemoryContext oldContext = NULL;
QueryDesc *queryDesc; QueryDesc *queryDesc;
EState *state; EState *state;
TupleDesc attinfo; TupleDesc attinfo;
...@@ -217,14 +220,18 @@ ProcessQuery(Query *parsetree, ...@@ -217,14 +220,18 @@ ProcessQuery(Query *parsetree,
if (isRetrieveIntoPortal) if (isRetrieveIntoPortal)
{ {
portal = PreparePortal(intoName); portal = PreparePortal(intoName);
/* CurrentMemoryContext is now pointing to portal's context */ oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal));
parsetree = copyObject(parsetree); parsetree = copyObject(parsetree);
plan = copyObject(plan); 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 * Now we can create the QueryDesc object.
* the portal context, if portal retrieve).
* ---------------- * ----------------
*/ */
queryDesc = CreateQueryDesc(parsetree, plan, dest); queryDesc = CreateQueryDesc(parsetree, plan, dest);
...@@ -241,7 +248,7 @@ ProcessQuery(Query *parsetree, ...@@ -241,7 +248,7 @@ ProcessQuery(Query *parsetree,
queryDesc->dest = (int) None; queryDesc->dest = (int) None;
/* ---------------- /* ----------------
* create a default executor state.. * create a default executor state.
* ---------------- * ----------------
*/ */
state = CreateExecutorState(); state = CreateExecutorState();
...@@ -279,9 +286,11 @@ ProcessQuery(Query *parsetree, ...@@ -279,9 +286,11 @@ ProcessQuery(Query *parsetree,
state, state,
PortalCleanup); PortalCleanup);
MemoryContextSwitchTo(TransactionCommandContext); /* Now we can return to caller's memory context. */
MemoryContextSwitchTo(oldContext);
EndCommand(tag, dest); EndCommand(tag, dest);
return; return;
} }
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -26,7 +26,7 @@
* This makes it easy and fast to release the storage when we don't need it * This makes it easy and fast to release the storage when we don't need it
* anymore. * 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 * 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 * 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.) * be cleaned up by the virtual file manager in event of an error.)
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* 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.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 ...@@ -223,9 +223,14 @@ typedef struct EState
uint32 es_processed; /* # of tuples processed */ uint32 es_processed; /* # of tuples processed */
Oid es_lastoid; /* last oid processed (by INSERT) */ Oid es_lastoid; /* last oid processed (by INSERT) */
List *es_rowMark; /* not good place, but there is no other */ 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; List **es_result_relation_constraints;
ExprContext *es_constraint_exprcontext;
/* Below is to re-evaluate plan qual in READ COMMITTED mode */ /* Below is to re-evaluate plan qual in READ COMMITTED mode */
struct Plan *es_origPlan; struct Plan *es_origPlan;
Pointer es_evalPlanQual; Pointer es_evalPlanQual;
......
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