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.
This commit is contained in:
parent
94e90d9a86
commit
0147b1934f
@ -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 @@
|
|||||||
#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,
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
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)
|
|||||||
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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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,
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
if (result)
|
if (result)
|
||||||
pfree(result);
|
pfree(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
FreeExprContext(econtext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -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)
|
|||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* 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)
|
|||||||
* 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 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* 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)
|
|||||||
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)
|
|||||||
|
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* 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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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,
|
|||||||
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 @@
|
|||||||
* 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 @@
|
|||||||
* 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 @@
|
|||||||
* 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
|
|||||||
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;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user