diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index a2180b3466..cf0c1de7c6 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.146 2002/12/13 19:45:45 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/bootstrap/bootstrap.c,v 1.147 2002/12/15 16:17:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1143,8 +1143,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = (List *) - ExecInitExpr((Expr *) newind->il_info->ii_Predicate, NULL); + newind->il_info->ii_PredicateState = NIL; newind->il_next = ILHead; ILHead = newind; diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 4e72fc53f0..5ccb70aee7 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.207 2002/12/13 19:45:47 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.208 2002/12/15 16:17:38 tgl Exp $ * * * INTERFACE ROUTINES @@ -41,7 +41,6 @@ #include "executor/executor.h" #include "miscadmin.h" #include "optimizer/clauses.h" -#include "optimizer/planmain.h" #include "optimizer/prep.h" #include "parser/parse_func.h" #include "storage/sinval.h" @@ -912,7 +911,6 @@ BuildIndexInfo(Form_pg_index indexStruct) /* * If partial index, convert predicate into expression nodetree - * and prepare an execution state nodetree for it */ if (VARSIZE(&indexStruct->indpred) > VARHDRSZ) { @@ -921,9 +919,7 @@ BuildIndexInfo(Form_pg_index indexStruct) predString = DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(&indexStruct->indpred))); ii->ii_Predicate = stringToNode(predString); - fix_opfuncids((Node *) ii->ii_Predicate); - ii->ii_PredicateState = (List *) - ExecInitExpr((Expr *) ii->ii_Predicate, NULL); + ii->ii_PredicateState = NIL; pfree(predString); } else @@ -1489,9 +1485,10 @@ IndexBuildHeapScan(Relation heapRelation, Datum attdata[INDEX_MAX_KEYS]; char nulls[INDEX_MAX_KEYS]; double reltuples; - List *predicate = indexInfo->ii_PredicateState; + List *predicate; TupleTable tupleTable; TupleTableSlot *slot; + EState *estate; ExprContext *econtext; Snapshot snapshot; TransactionId OldestXmin; @@ -1503,28 +1500,42 @@ IndexBuildHeapScan(Relation heapRelation, heapDescriptor = RelationGetDescr(heapRelation); + /* + * Need an EState for evaluation of functional-index functions + * and partial-index predicates. + */ + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + /* * If this is a predicate (partial) index, we will need to evaluate * the predicate using ExecQual, which requires the current tuple to - * be in a slot of a TupleTable. In addition, ExecQual must have an - * ExprContext referring to that slot. Here, we initialize dummy - * TupleTable and ExprContext objects for this purpose. --Nels, Feb 92 - * - * We construct the ExprContext anyway since we need a per-tuple - * temporary memory context for function evaluation -- tgl July 00 + * be in a slot of a TupleTable. */ - if (predicate != NIL) + if (indexInfo->ii_Predicate != NIL) { tupleTable = ExecCreateTupleTable(1); slot = ExecAllocTableSlot(tupleTable); ExecSetSlotDescriptor(slot, heapDescriptor, false); + + /* Arrange for econtext's scan tuple to be the tuple under test */ + econtext->ecxt_scantuple = slot; + + /* + * Set up execution state for predicate. Note: we mustn't attempt to + * cache this in the indexInfo, since we're building it in a transient + * EState. + */ + predicate = (List *) + ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, + estate); } else { tupleTable = NULL; slot = NULL; + predicate = NIL; } - econtext = MakeExprContext(slot, TransactionCommandContext); /* * Ok, begin our scan of the base relation. We use SnapshotAny @@ -1687,9 +1698,10 @@ IndexBuildHeapScan(Relation heapRelation, heap_endscan(scan); - if (predicate != NIL) + if (tupleTable) ExecDropTupleTable(tupleTable, true); - FreeExprContext(econtext); + + FreeExecutorState(estate); return reltuples; } diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 9e8f7a46be..fd8c6b83a8 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.186 2002/12/13 19:45:48 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/copy.c,v 1.187 2002/12/15 16:17:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,7 +35,6 @@ #include "mb/pg_wchar.h" #include "miscadmin.h" #include "nodes/makefuncs.h" -#include "optimizer/planmain.h" #include "parser/parse_coerce.h" #include "parser/parse_relation.h" #include "rewrite/rewriteHandler.h" @@ -803,6 +802,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, slot = ExecAllocTableSlot(tupleTable); ExecSetSlotDescriptor(slot, tupDesc, false); + econtext = GetPerTupleExprContext(estate); + /* * Pick up the required catalog information for each attribute in the * relation, including the input function, the element type (to pass @@ -841,8 +842,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, if (defexpr != NULL) { - fix_opfuncids(defexpr); - defexprs[num_defaults] = ExecInitExpr((Expr *) defexpr, NULL); + defexprs[num_defaults] = ExecPrepareExpr((Expr *) defexpr, + estate); defmap[num_defaults] = i; num_defaults++; } @@ -873,8 +874,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, /* check whether any constraints actually found */ if (node != (Node *) prm) { - fix_opfuncids(node); - constraintexprs[i] = ExecInitExpr((Expr *) node, NULL); + constraintexprs[i] = ExecPrepareExpr((Expr *) node, + estate); hasConstraints = true; } } @@ -934,8 +935,6 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, copy_lineno = 0; fe_eof = false; - econtext = GetPerTupleExprContext(estate); - /* Make room for a PARAM_EXEC value for domain constraint checks */ if (hasConstraints) econtext->ecxt_param_exec_vals = (ParamExecData *) @@ -953,9 +952,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, /* Reset the per-tuple exprcontext */ ResetPerTupleExprContext(estate); - /* Switch to and reset per-tuple memory context, too */ + /* Switch into its memory context */ MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - MemoryContextReset(CurrentMemoryContext); /* Initialize all values for row to NULL */ MemSet(values, 0, num_phys_attrs * sizeof(Datum)); @@ -1268,6 +1266,8 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids, ExecDropTupleTable(tupleTable, true); ExecCloseIndices(resultRelInfo); + + FreeExecutorState(estate); } diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index 05815a2201..3820dd8b46 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -5,7 +5,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994-5, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.98 2002/12/14 00:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.99 2002/12/15 16:17:38 tgl Exp $ * */ @@ -206,6 +206,8 @@ ExplainOneQuery(Query *query, ExplainStmt *stmt, TupOutputState *tstate) gettimeofday(&starttime, NULL); ExecutorEnd(queryDesc); + FreeQueryDesc(queryDesc); + CommandCounterIncrement(); totaltime += elapsed_time(&starttime); diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 7b66eea0b0..d46b7a389a 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.94 2002/12/13 19:45:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/indexcmds.c,v 1.95 2002/12/15 16:17:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,6 @@ #include "executor/executor.h" #include "miscadmin.h" #include "optimizer/clauses.h" -#include "optimizer/planmain.h" #include "optimizer/prep.h" #include "parser/parsetree.h" #include "parser/parse_coerce.h" @@ -163,7 +162,6 @@ DefineIndex(RangeVar *heapRelation, if (predicate) { cnfPred = canonicalize_qual((Expr *) copyObject(predicate), true); - fix_opfuncids((Node *) cnfPred); CheckPredicate(cnfPred, rangetable, relationId); } @@ -173,8 +171,7 @@ DefineIndex(RangeVar *heapRelation, */ indexInfo = makeNode(IndexInfo); indexInfo->ii_Predicate = cnfPred; - indexInfo->ii_PredicateState = (List *) - ExecInitExpr((Expr *) cnfPred, NULL); + indexInfo->ii_PredicateState = NIL; indexInfo->ii_FuncOid = InvalidOid; indexInfo->ii_Unique = unique; diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c index c8607fcf32..b1799e49f8 100644 --- a/src/backend/commands/portalcmds.c +++ b/src/backend/commands/portalcmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.5 2002/12/05 15:50:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/portalcmds.c,v 1.6 2002/12/15 16:17:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -31,28 +31,22 @@ void PortalCleanup(Portal portal) { - MemoryContext oldcontext; - /* * sanity checks */ AssertArg(PortalIsValid(portal)); AssertArg(portal->cleanup == PortalCleanup); - /* - * set proper portal-executor context before calling ExecMain. - */ - oldcontext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); - /* * tell the executor to shutdown the query */ ExecutorEnd(PortalGetQueryDesc(portal)); /* - * switch back to previous context + * This should be unnecessary since the querydesc should be in the + * portal's memory context, but do it anyway for symmetry. */ - MemoryContextSwitchTo(oldcontext); + FreeQueryDesc(PortalGetQueryDesc(portal)); } diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c index 98894372fa..ece9802dc4 100644 --- a/src/backend/commands/prepare.c +++ b/src/backend/commands/prepare.c @@ -6,7 +6,7 @@ * Copyright (c) 2002, PostgreSQL Global Development Group * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.10 2002/12/13 19:45:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/prepare.c,v 1.11 2002/12/15 16:17:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,7 +15,6 @@ #include "commands/prepare.h" #include "executor/executor.h" #include "utils/guc.h" -#include "optimizer/planmain.h" #include "optimizer/planner.h" #include "rewrite/rewriteHandler.h" #include "tcop/pquery.h" @@ -50,7 +49,6 @@ static void InitQueryHashTable(void); static void StoreQuery(const char *stmt_name, List *query_list, List *plan_list, List *argtype_list); static QueryHashEntry *FetchQuery(const char *plan_name); -static void RunQuery(QueryDesc *qdesc); /* @@ -96,33 +94,37 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) *query_list, *plan_list; ParamListInfo paramLI = NULL; + EState *estate; /* Look it up in the hash table */ entry = FetchQuery(stmt->name); - /* Make working copies the executor can safely scribble on */ - query_list = (List *) copyObject(entry->query_list); - plan_list = (List *) copyObject(entry->plan_list); + query_list = entry->query_list; + plan_list = entry->plan_list; Assert(length(query_list) == length(plan_list)); + /* + * Need an EState to evaluate parameters; must not delete it till end + * of query, in case parameters are pass-by-reference. + */ + estate = CreateExecutorState(); + /* Evaluate parameters, if any */ if (entry->argtype_list != NIL) { int nargs = length(entry->argtype_list); int i = 0; List *exprstates; - ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext); /* Parser should have caught this error, but check */ if (nargs != length(stmt->params)) elog(ERROR, "ExecuteQuery: wrong number of arguments"); - fix_opfuncids((Node *) stmt->params); + exprstates = (List *) ExecPrepareExpr((Expr *) stmt->params, estate); - exprstates = (List *) ExecInitExpr((Expr *) stmt->params, NULL); - - paramLI = (ParamListInfo) palloc0((nargs + 1) * sizeof(ParamListInfoData)); + paramLI = (ParamListInfo) + palloc0((nargs + 1) * sizeof(ParamListInfoData)); foreach(l, exprstates) { @@ -130,7 +132,7 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) bool isNull; paramLI[i].value = ExecEvalExprSwitchContext(n, - econtext, + GetPerTupleExprContext(estate), &isNull, NULL); paramLI[i].kind = PARAM_NUM; @@ -173,7 +175,13 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) qdesc->dest = None; } - RunQuery(qdesc); + ExecutorStart(qdesc); + + ExecutorRun(qdesc, ForwardScanDirection, 0L); + + ExecutorEnd(qdesc); + + FreeQueryDesc(qdesc); if (log_executor_stats) ShowUsage("EXECUTOR STATISTICS"); @@ -188,7 +196,9 @@ ExecuteQuery(ExecuteStmt *stmt, CommandDest outputDest) CommandCounterIncrement(); } - /* No need to pfree memory, MemoryContext will be reset */ + FreeExecutorState(estate); + + /* No need to pfree other memory, MemoryContext will be reset */ } /* @@ -333,17 +343,6 @@ FetchQueryParams(const char *plan_name) return entry->argtype_list; } -/* - * Actually execute a prepared query. - */ -static void -RunQuery(QueryDesc *qdesc) -{ - ExecutorStart(qdesc); - ExecutorRun(qdesc, ForwardScanDirection, 0L); - ExecutorEnd(qdesc); -} - /* * Implements the 'DEALLOCATE' utility statement: deletes the * specified plan from storage. diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index a7a19c6f74..09c60bdf3f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.60 2002/12/13 19:45:51 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/tablecmds.c,v 1.61 2002/12/15 16:17:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,7 +37,6 @@ #include "nodes/makefuncs.h" #include "optimizer/clauses.h" #include "optimizer/plancat.h" -#include "optimizer/planmain.h" #include "optimizer/prep.h" #include "parser/gramparse.h" #include "parser/parse_coerce.h" @@ -2713,6 +2712,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) ParseState *pstate; bool successful = true; HeapScanDesc scan; + EState *estate; ExprContext *econtext; TupleTableSlot *slot; HeapTuple tuple; @@ -2723,9 +2723,7 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) /* * We need to make a parse state and range - * table to allow us to transformExpr and - * fix_opfuncids to get a version of the - * expression we can pass to ExecQual + * table to allow us to do transformExpr() */ pstate = make_parsestate(NULL); rte = addRangeTableEntryForRelation(pstate, @@ -2765,19 +2763,22 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) */ expr = eval_const_expressions(expr); - /* And fix the opfuncids */ - fix_opfuncids(expr); + /* Needs to be in implicit-ANDs form for ExecQual */ + qual = make_ands_implicit((Expr *) expr); - qual = makeList1(expr); + /* Need an EState to run ExecQual */ + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); /* build execution state for qual */ - qualstate = (List *) ExecInitExpr((Expr *) qual, NULL); + qualstate = (List *) ExecPrepareExpr((Expr *) qual, estate); /* Make tuple slot to hold tuples */ slot = MakeTupleTableSlot(); ExecSetSlotDescriptor(slot, RelationGetDescr(rel), false); - /* Make an expression context for ExecQual */ - econtext = MakeExprContext(slot, CurrentMemoryContext); + + /* Arrange for econtext's scan tuple to be the tuple under test */ + econtext->ecxt_scantuple = slot; /* * Scan through the rows now, checking the expression at each row. @@ -2797,8 +2798,8 @@ AlterTableAddCheckConstraint(Relation rel, Constraint *constr) heap_endscan(scan); - FreeExprContext(econtext); pfree(slot); + FreeExecutorState(estate); if (!successful) elog(ERROR, "AlterTableAddConstraint: rejected due to CHECK constraint %s", diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index fc0030fe76..c088aaac99 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.24 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/typecmds.c,v 1.25 2002/12/15 16:17:43 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -47,7 +47,6 @@ #include "miscadmin.h" #include "nodes/nodes.h" #include "optimizer/clauses.h" -#include "optimizer/planmain.h" #include "optimizer/var.h" #include "parser/parse_coerce.h" #include "parser/parse_expr.h" @@ -1242,6 +1241,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) List *rels; List *rt; Form_pg_type typTup; + EState *estate; ExprContext *econtext; char *ccbin; Expr *expr; @@ -1338,11 +1338,13 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) * the constraint is being added to. */ expr = (Expr *) stringToNode(ccbin); - fix_opfuncids((Node *) expr); - exprstate = ExecInitExpr(expr, NULL); - /* Make an expression context for ExecEvalExpr */ - econtext = MakeExprContext(NULL, CurrentMemoryContext); + /* Need an EState to run ExecEvalExpr */ + estate = CreateExecutorState(); + econtext = GetPerTupleExprContext(estate); + + /* build execution state for expr */ + exprstate = ExecPrepareExpr(expr, estate); rels = get_rels_with_domain(domainoid); @@ -1377,7 +1379,9 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) econtext->domainValue_datum = d; econtext->domainValue_isNull = isNull; - conResult = ExecEvalExpr(exprstate, econtext, &isNull, NULL); + conResult = ExecEvalExprSwitchContext(exprstate, + econtext, + &isNull, NULL); if (!isNull && !DatumGetBool(conResult)) elog(ERROR, "AlterDomainAddConstraint: Domain %s constraint %s failed", @@ -1393,7 +1397,7 @@ AlterDomainAddConstraint(List *names, Node *newConstraint) heap_close(testrel, NoLock); } - FreeExprContext(econtext); + FreeExecutorState(estate); /* Clean up */ heap_close(rel, NoLock); diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 5108268925..48870b4bca 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -13,7 +13,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.244 2002/10/31 19:25:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.245 2002/12/15 16:17:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1437,6 +1437,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, * We need a ResultRelInfo and an EState so we can use the regular * executor's index-entry-making machinery. */ + estate = CreateExecutorState(); + resultRelInfo = makeNode(ResultRelInfo); resultRelInfo->ri_RangeTableIndex = 1; /* dummy */ resultRelInfo->ri_RelationDesc = onerel; @@ -1444,7 +1446,6 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ExecOpenIndices(resultRelInfo); - estate = CreateExecutorState(); estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; @@ -2484,6 +2485,8 @@ repair_frag(VRelStats *vacrelstats, Relation onerel, ExecDropTupleTable(tupleTable, true); ExecCloseIndices(resultRelInfo); + + FreeExecutorState(estate); } /* diff --git a/src/backend/executor/README b/src/backend/executor/README index d9b0ea1275..ebdbe2d9d0 100644 --- a/src/backend/executor/README +++ b/src/backend/executor/README @@ -1,4 +1,4 @@ -$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.2 2002/12/05 15:50:30 tgl Exp $ +$Header: /cvsroot/pgsql/src/backend/executor/README,v 1.3 2002/12/15 16:17:45 tgl Exp $ The Postgres Executor --------------------- @@ -60,6 +60,83 @@ ExprState nodes. (Actually, there are also List nodes, which are used as "glue" in all four kinds of tree.) +Memory Management +----------------- + +A "per query" memory context is created during CreateExecutorState(); +all storage allocated during an executor invocation is allocated in that +context or a child context. This allows easy reclamation of storage +during executor shutdown --- rather than messing with retail pfree's and +probable storage leaks, we just destroy the memory context. + +In particular, the plan state trees and expression state trees described +in the previous section are allocated in the per-query memory context. + +To avoid intra-query memory leaks, most processing while a query runs +is done in "per tuple" memory contexts, which are so-called because they +are typically reset to empty once per tuple. Per-tuple contexts are usually +associated with ExprContexts, and commonly each PlanState node has its own +ExprContext to evaluate its qual and targetlist expressions in. + + +Query Processing Control Flow +----------------------------- + +This is a sketch of control flow for full query processing: + + CreateQueryDesc + + ExecutorStart + CreateExecutorState + creates per-query context + switch to per-query context to run ExecInitNode + ExecInitNode --- recursively scans plan tree + CreateExprContext + creates per-tuple context + ExecInitExpr + + ExecutorRun + ExecProcNode --- recursively called in per-query context + ExecEvalExpr --- called in per-tuple context + ResetExprContext --- to free memory + + ExecutorEnd + ExecEndNode --- recursively releases resources + FreeExecutorState + frees per-query context and child contexts + + FreeQueryDesc + +Per above comments, it's not really critical for ExecEndNode to free any +memory; it'll all go away in FreeExecutorState anyway. However, we do need to +be careful to close relations, drop buffer pins, etc, so we do need to scan +the plan state tree to find these sorts of resources. + + +The executor can also be used to evaluate simple expressions without any Plan +tree ("simple" meaning "no aggregates and no sub-selects", though such might +be hidden inside function calls). This case has a flow of control like + + CreateExecutorState + creates per-query context + + CreateExprContext -- or use GetPerTupleExprContext(estate) + creates per-tuple context + + ExecPrepareExpr + switch to per-query context to run ExecInitExpr + ExecInitExpr + + Repeatedly do: + ExecEvalExprSwitchContext + ExecEvalExpr --- called in per-tuple context + ResetExprContext --- to free memory + + FreeExecutorState + frees per-query context, as well as ExprContext + (a separate FreeExprContext call is not necessary) + + EvalPlanQual (READ COMMITTED update checking) --------------------------------------------- diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index bac7398825..a25f2f2e29 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -26,7 +26,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.192 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.193 2002/12/15 16:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,7 +40,6 @@ #include "executor/execdebug.h" #include "executor/execdefs.h" #include "miscadmin.h" -#include "optimizer/planmain.h" #include "optimizer/var.h" #include "parser/parsetree.h" #include "utils/acl.h" @@ -53,7 +52,6 @@ static void initResultRelInfo(ResultRelInfo *resultRelInfo, Index resultRelationIndex, List *rangeTable, CmdType operation); -static void EndPlan(PlanState *planstate, EState *estate); static TupleTableSlot *ExecutePlan(EState *estate, PlanState *planstate, CmdType operation, long numberTuples, @@ -86,27 +84,31 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation); * field of the QueryDesc is filled in to describe the tuples that will be * returned, and the internal fields (estate and planstate) are set up. * - * XXX this will change soon: - * NB: the CurrentMemoryContext when this is called must be the context - * to be used as the per-query context for the query plan. ExecutorRun() - * and ExecutorEnd() must be called in this same memory context. + * NB: the CurrentMemoryContext when this is called will become the parent + * of the per-query context used for this Executor invocation. * ---------------------------------------------------------------- */ void ExecutorStart(QueryDesc *queryDesc) { EState *estate; + MemoryContext oldcontext; /* sanity checks: queryDesc must not be started already */ Assert(queryDesc != NULL); Assert(queryDesc->estate == NULL); /* - * Build EState, fill with parameters from queryDesc + * Build EState, switch into per-query memory context for startup. */ estate = CreateExecutorState(); queryDesc->estate = estate; + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* + * Fill in parameters, if any, from queryDesc + */ estate->es_param_list_info = queryDesc->params; if (queryDesc->plantree->nParamExec > 0) @@ -128,6 +130,8 @@ ExecutorStart(QueryDesc *queryDesc) * Initialize the plan state tree */ InitPlan(queryDesc); + + MemoryContextSwitchTo(oldcontext); } /* ---------------------------------------------------------------- @@ -152,23 +156,30 @@ TupleTableSlot * ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count) { - CmdType operation; EState *estate; + CmdType operation; CommandDest dest; DestReceiver *destfunc; TupleTableSlot *result; + MemoryContext oldcontext; + + /* sanity checks */ + Assert(queryDesc != NULL); + + estate = queryDesc->estate; + + Assert(estate != NULL); /* - * sanity checks + * Switch into per-query memory context */ - Assert(queryDesc != NULL); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* * extract information from the query descriptor and the query * feature. */ operation = queryDesc->operation; - estate = queryDesc->estate; dest = queryDesc->dest; /* @@ -199,6 +210,8 @@ ExecutorRun(QueryDesc *queryDesc, */ (*destfunc->cleanup) (destfunc); + MemoryContextSwitchTo(oldcontext); + return result; } @@ -213,72 +226,37 @@ void ExecutorEnd(QueryDesc *queryDesc) { EState *estate; + MemoryContext oldcontext; /* sanity checks */ Assert(queryDesc != NULL); estate = queryDesc->estate; - EndPlan(queryDesc->planstate, estate); - - if (estate->es_snapshot != NULL) - { - if (estate->es_snapshot->xcnt > 0) - pfree(estate->es_snapshot->xip); - pfree(estate->es_snapshot); - estate->es_snapshot = NULL; - } - - if (estate->es_param_exec_vals != NULL) - { - pfree(estate->es_param_exec_vals); - estate->es_param_exec_vals = NULL; - } -} - - -/* - * CreateExecutorState - */ -EState * -CreateExecutorState(void) -{ - EState *state; + Assert(estate != NULL); /* - * create a new executor state + * Switch into per-query memory context to run ExecEndPlan */ - state = makeNode(EState); + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + ExecEndPlan(queryDesc->planstate, estate); /* - * initialize the Executor State structure + * Must switch out of context before destroying it */ - state->es_direction = ForwardScanDirection; - state->es_range_table = NIL; - - state->es_result_relations = NULL; - state->es_num_result_relations = 0; - state->es_result_relation_info = NULL; - - state->es_junkFilter = NULL; - - state->es_into_relation_descriptor = NULL; - - state->es_param_list_info = NULL; - state->es_param_exec_vals = NULL; - - state->es_tupleTable = NULL; - - state->es_query_cxt = CurrentMemoryContext; - - state->es_instrument = false; - - state->es_per_tuple_exprcontext = NULL; + MemoryContextSwitchTo(oldcontext); /* - * return the executor state structure + * Release EState and per-query memory context. This should release + * everything the executor has allocated. */ - return state; + FreeExecutorState(estate); + + /* Reset queryDesc fields that no longer point to anything */ + queryDesc->tupDesc = NULL; + queryDesc->estate = NULL; + queryDesc->planstate = NULL; } @@ -794,13 +772,13 @@ initResultRelInfo(ResultRelInfo *resultRelInfo, } /* ---------------------------------------------------------------- - * EndPlan + * ExecEndPlan * * Cleans up the query plan -- closes files and frees up storage * ---------------------------------------------------------------- */ -static void -EndPlan(PlanState *planstate, EState *estate) +void +ExecEndPlan(PlanState *planstate, EState *estate) { ResultRelInfo *resultRelInfo; int i; @@ -1542,9 +1520,8 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, for (i = 0; i < ncheck; i++) { qual = (List *) stringToNode(check[i].ccbin); - fix_opfuncids((Node *) qual); resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecInitExpr((Expr *) qual, NULL); + ExecPrepareExpr((Expr *) qual, estate); } MemoryContextSwitchTo(oldContext); } diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index a3f79c3ac8..971773b121 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.120 2002/12/14 00:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.121 2002/12/15 16:17:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,6 +40,7 @@ #include "executor/functions.h" #include "executor/nodeSubplan.h" #include "miscadmin.h" +#include "optimizer/planmain.h" #include "parser/parse_expr.h" #include "utils/acl.h" #include "utils/array.h" @@ -1896,9 +1897,11 @@ ExecEvalExprSwitchContext(ExprState *expression, * cleanup work can register a shutdown callback in the ExprContext. * * 'node' is the root of the expression tree to examine - * 'parent' is the PlanState node that owns the expression, - * or NULL if we are preparing an expression that is not associated - * with a plan. (If so, it can't have aggs or subplans.) + * 'parent' is the PlanState node that owns the expression. + * + * 'parent' may be NULL if we are preparing an expression that is not + * associated with a plan tree. (If so, it can't have aggs or subplans.) + * This case should usually come through ExecPrepareExpr, not directly here. */ ExprState * ExecInitExpr(Expr *node, PlanState *parent) @@ -2017,6 +2020,7 @@ ExecInitExpr(Expr *node, PlanState *parent) * parent->subPlan. The subplans will be initialized later. */ parent->subPlan = lcons(sstate, parent->subPlan); + sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->oper = (List *) @@ -2149,6 +2153,7 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent) elog(ERROR, "ExecInitExpr: SubPlan not expected here"); /* The subplan's state will be initialized later */ + sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->oper = (List *) ExecInitExpr((Expr *) node->oper, parent); @@ -2159,6 +2164,33 @@ ExecInitExprInitPlan(SubPlan *node, PlanState *parent) return sstate; } +/* + * ExecPrepareExpr --- initialize for expression execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we apply + * fix_opfuncids() to the passed expression tree to be sure it is ready + * to run. (In ordinary Plan trees the planner will have fixed opfuncids, + * but callers outside the executor will not have done this.) + */ +ExprState * +ExecPrepareExpr(Expr *node, EState *estate) +{ + ExprState *result; + MemoryContext oldcontext; + + fix_opfuncids((Node *) node); + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + result = ExecInitExpr(node, NULL); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index 36997a4910..6c2cece7b6 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -8,13 +8,20 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.92 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.93 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES + * CreateExecutorState Create/delete executor working state + * FreeExecutorState + * CreateExprContext + * FreeExprContext + * * ExecAssignExprContext Common code for plan node init routines. + * ExecAssignResultType + * etc * * ExecOpenIndices \ * ExecCloseIndices | referenced by InitPlan, EndPlan, @@ -26,7 +33,6 @@ * NOTES * This file has traditionally been the place to stick misc. * executor support stuff that doesn't really go anyplace else. - * */ #include "postgres.h" @@ -64,6 +70,7 @@ extern int NIndexTupleProcessed; /* have to be defined in the static void ShutdownExprContext(ExprContext *econtext); + /* ---------------------------------------------------------------- * statistic functions * ---------------------------------------------------------------- @@ -124,136 +131,263 @@ DisplayTupleCount(FILE *statfp) } #endif + /* ---------------------------------------------------------------- - * miscellaneous node-init support functions + * Executor state and memory management functions * ---------------------------------------------------------------- */ /* ---------------- - * ExecAssignExprContext + * CreateExecutorState * - * This initializes the ExprContext field. It is only necessary - * to do this for nodes which use ExecQual or ExecProject - * because those routines depend on econtext. Other nodes that - * don't have to evaluate expressions don't need to do this. + * Create and initialize an EState node, which is the root of + * working storage for an entire Executor invocation. * - * Note: we assume CurrentMemoryContext is the correct per-query context. - * This should be true during plan node initialization. + * Principally, this creates the per-query memory context that will be + * used to hold all working data that lives till the end of the query. + * Note that the per-query context will become a child of the caller's + * CurrentMemoryContext. + * ---------------- + */ +EState * +CreateExecutorState(void) +{ + EState *estate; + MemoryContext qcontext; + MemoryContext oldcontext; + + /* + * Create the per-query context for this Executor run. + */ + qcontext = AllocSetContextCreate(CurrentMemoryContext, + "ExecutorState", + ALLOCSET_DEFAULT_MINSIZE, + ALLOCSET_DEFAULT_INITSIZE, + ALLOCSET_DEFAULT_MAXSIZE); + + /* + * Make the EState node within the per-query context. This way, + * we don't need a separate pfree() operation for it at shutdown. + */ + oldcontext = MemoryContextSwitchTo(qcontext); + + estate = makeNode(EState); + + /* + * Initialize all fields of the Executor State structure + */ + estate->es_direction = ForwardScanDirection; + estate->es_snapshot = SnapshotNow; + estate->es_range_table = NIL; + + estate->es_result_relations = NULL; + estate->es_num_result_relations = 0; + estate->es_result_relation_info = NULL; + + estate->es_junkFilter = NULL; + estate->es_into_relation_descriptor = NULL; + + estate->es_param_list_info = NULL; + estate->es_param_exec_vals = NULL; + + estate->es_query_cxt = qcontext; + + estate->es_tupleTable = NULL; + + estate->es_processed = 0; + estate->es_lastoid = InvalidOid; + estate->es_rowMark = NIL; + + estate->es_instrument = false; + + estate->es_exprcontexts = NIL; + + estate->es_per_tuple_exprcontext = NULL; + + estate->es_origPlan = NULL; + estate->es_evalPlanQual = NULL; + estate->es_evTupleNull = NULL; + estate->es_evTuple = NULL; + estate->es_useEvalPlan = false; + + /* + * Return the executor state structure + */ + MemoryContextSwitchTo(oldcontext); + + return estate; +} + +/* ---------------- + * FreeExecutorState + * + * Release an EState along with all remaining working storage. + * + * Note: this is not responsible for releasing non-memory resources, + * such as open relations or buffer pins. But it will shut down any + * still-active ExprContexts within the EState. That is sufficient + * cleanup for situations where the EState has only been used for expression + * evaluation, and not to run a complete Plan. + * + * This can be called in any memory context ... so long as it's not one + * of the ones to be freed. * ---------------- */ void -ExecAssignExprContext(EState *estate, PlanState *planstate) +FreeExecutorState(EState *estate) { - ExprContext *econtext = makeNode(ExprContext); + /* + * Shut down and free any remaining ExprContexts. We do this + * explicitly to ensure that any remaining shutdown callbacks get + * called (since they might need to release resources that aren't + * simply memory within the per-query memory context). + */ + while (estate->es_exprcontexts) + { + FreeExprContext((ExprContext *) lfirst(estate->es_exprcontexts)); + /* FreeExprContext removed the list link for us */ + } + /* + * Free the per-query memory context, thereby releasing all working + * memory, including the EState node itself. + */ + MemoryContextDelete(estate->es_query_cxt); +} +/* ---------------- + * CreateExprContext + * + * Create a context for expression evaluation within an EState. + * + * An executor run may require multiple ExprContexts (we usually make one + * for each Plan node, and a separate one for per-output-tuple processing + * such as constraint checking). Each ExprContext has its own "per-tuple" + * memory context. + * + * Note we make no assumption about the caller's memory context. + * ---------------- + */ +ExprContext * +CreateExprContext(EState *estate) +{ + ExprContext *econtext; + MemoryContext oldcontext; + + /* Create the ExprContext node within the per-query memory context */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + econtext = makeNode(ExprContext); + + /* Initialize fields of ExprContext */ econtext->ecxt_scantuple = NULL; econtext->ecxt_innertuple = NULL; econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = CurrentMemoryContext; + + econtext->ecxt_per_query_memory = estate->es_query_cxt; /* * Create working memory for expression evaluation in this context. */ econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "PlanExprContext", + AllocSetContextCreate(estate->es_query_cxt, + "ExprContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); + econtext->ecxt_param_exec_vals = estate->es_param_exec_vals; econtext->ecxt_param_list_info = estate->es_param_list_info; + econtext->ecxt_aggvalues = NULL; econtext->ecxt_aggnulls = NULL; + + econtext->domainValue_datum = (Datum) 0; + econtext->domainValue_isNull = true; + + econtext->ecxt_estate = estate; + econtext->ecxt_callbacks = NULL; - planstate->ps_ExprContext = econtext; -} - -/* ---------------- - * MakeExprContext - * - * Build an expression context for use outside normal plan-node cases. - * A fake scan-tuple slot can be supplied (pass NULL if not needed). - * A memory context sufficiently long-lived to use as fcache context - * must be supplied as well. - * ---------------- - */ -ExprContext * -MakeExprContext(TupleTableSlot *slot, - MemoryContext queryContext) -{ - ExprContext *econtext = makeNode(ExprContext); - - econtext->ecxt_scantuple = slot; - econtext->ecxt_innertuple = NULL; - econtext->ecxt_outertuple = NULL; - econtext->ecxt_per_query_memory = queryContext; - /* - * We make the temporary context a child of current working context, - * not of the specified queryContext. This seems reasonable but I'm - * not totally sure about it... - * - * Expression contexts made via this routine typically don't live long - * enough to get reset, so specify a minsize of 0. That avoids - * alloc'ing any memory in the common case where expr eval doesn't use - * any. + * Link the ExprContext into the EState to ensure it is shut down + * when the EState is freed. Because we use lcons(), shutdowns will + * occur in reverse order of creation, which may not be essential + * but can't hurt. */ - econtext->ecxt_per_tuple_memory = - AllocSetContextCreate(CurrentMemoryContext, - "TempExprContext", - 0, - ALLOCSET_DEFAULT_INITSIZE, - ALLOCSET_DEFAULT_MAXSIZE); - econtext->ecxt_param_exec_vals = NULL; - econtext->ecxt_param_list_info = NULL; - econtext->ecxt_aggvalues = NULL; - econtext->ecxt_aggnulls = NULL; - econtext->ecxt_callbacks = NULL; + estate->es_exprcontexts = lcons(econtext, estate->es_exprcontexts); + + MemoryContextSwitchTo(oldcontext); return econtext; } -/* - * Free an ExprContext made by MakeExprContext, including the temporary - * context used for expression evaluation. Note this will cause any - * pass-by-reference expression result to go away! +/* ---------------- + * FreeExprContext + * + * Free an expression context, including calling any remaining + * shutdown callbacks. + * + * Since we free the temporary context used for expression evaluation, + * any previously computed pass-by-reference expression result will go away! + * + * Note we make no assumption about the caller's memory context. + * ---------------- */ void FreeExprContext(ExprContext *econtext) { + EState *estate; + /* Call any registered callbacks */ ShutdownExprContext(econtext); /* And clean up the memory used */ MemoryContextDelete(econtext->ecxt_per_tuple_memory); + /* Unlink self from owning EState */ + estate = econtext->ecxt_estate; + estate->es_exprcontexts = lremove(econtext, estate->es_exprcontexts); + /* And delete the ExprContext node */ pfree(econtext); } /* * Build a per-output-tuple ExprContext for an EState. * - * This is normally invoked via GetPerTupleExprContext() macro. + * This is normally invoked via GetPerTupleExprContext() macro, + * not directly. */ ExprContext * MakePerTupleExprContext(EState *estate) { if (estate->es_per_tuple_exprcontext == NULL) - { - MemoryContext oldContext; + estate->es_per_tuple_exprcontext = CreateExprContext(estate); - oldContext = MemoryContextSwitchTo(estate->es_query_cxt); - estate->es_per_tuple_exprcontext = - MakeExprContext(NULL, estate->es_query_cxt); - MemoryContextSwitchTo(oldContext); - } return estate->es_per_tuple_exprcontext; } + /* ---------------------------------------------------------------- - * Result slot tuple type and ProjectionInfo support + * miscellaneous node-init support functions + * + * Note: all of these are expected to be called with CurrentMemoryContext + * equal to the per-query memory context. * ---------------------------------------------------------------- */ +/* ---------------- + * ExecAssignExprContext + * + * This initializes the ps_ExprContext field. It is only necessary + * to do this for nodes which use ExecQual or ExecProject + * because those routines require an econtext. Other nodes that + * don't have to evaluate expressions don't need to do this. + * ---------------- + */ +void +ExecAssignExprContext(EState *estate, PlanState *planstate) +{ + planstate->ps_ExprContext = CreateExprContext(estate); +} + /* ---------------- * ExecAssignResultType * ---------------- @@ -367,35 +501,13 @@ ExecAssignProjectionInfo(PlanState *planstate) } -/* ---------------- - * ExecFreeProjectionInfo - * ---------------- - */ -void -ExecFreeProjectionInfo(PlanState *planstate) -{ - ProjectionInfo *projInfo; - - /* - * get projection info. if NULL then this node has none so we just - * return. - */ - projInfo = planstate->ps_ProjInfo; - if (projInfo == NULL) - return; - - /* - * clean up memory used. - */ - if (projInfo->pi_tupValue != NULL) - pfree(projInfo->pi_tupValue); - - pfree(projInfo); - planstate->ps_ProjInfo = NULL; -} - /* ---------------- * ExecFreeExprContext + * + * A plan node's ExprContext should be freed explicitly during ExecEndNode + * because there may be shutdown callbacks to call. (Other resources made + * by the above routines, such as projection info, don't need to be freed + * explicitly because they're just memory in the per-query memory context.) * ---------------- */ void @@ -411,16 +523,8 @@ ExecFreeExprContext(PlanState *planstate) if (econtext == NULL) return; - /* - * clean up any registered callbacks - */ - ShutdownExprContext(econtext); + FreeExprContext(econtext); - /* - * clean up memory used. - */ - MemoryContextDelete(econtext->ecxt_per_tuple_memory); - pfree(econtext); planstate->ps_ExprContext = NULL; } @@ -612,7 +716,8 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) } /* - * XXX should free indexInfo array here too. + * XXX should free indexInfo array here too? Currently we assume that + * such stuff will be cleaned up automatically in FreeExecutorState. */ } @@ -674,16 +779,31 @@ ExecInsertIndexTuples(TupleTableSlot *slot, for (i = 0; i < numIndices; i++) { IndexInfo *indexInfo; - List *predicate; InsertIndexResult result; if (relationDescs[i] == NULL) continue; indexInfo = indexInfoArray[i]; - predicate = indexInfo->ii_PredicateState; - if (predicate != NIL) + + /* Check for partial index */ + if (indexInfo->ii_Predicate != NIL) { + List *predicate; + + /* + * If predicate state not set up yet, create it (in the + * estate's per-query context) + */ + predicate = indexInfo->ii_PredicateState; + if (predicate == NIL) + { + predicate = (List *) + ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, + estate); + indexInfo->ii_PredicateState = predicate; + } + /* Skip this index-update if the predicate isn't satisfied */ if (!ExecQual(predicate, econtext, false)) continue; @@ -811,6 +931,17 @@ static void ShutdownExprContext(ExprContext *econtext) { ExprContext_CB *ecxt_callback; + MemoryContext oldcontext; + + /* Fast path in normal case where there's nothing to do. */ + if (econtext->ecxt_callbacks == NULL) + return; + + /* + * Call the callbacks in econtext's per-tuple context. This ensures + * that any memory they might leak will get cleaned up. + */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Call each callback function in reverse registration order. @@ -821,4 +952,6 @@ ShutdownExprContext(ExprContext *econtext) (*ecxt_callback->function) (ecxt_callback->arg); pfree(ecxt_callback); } + + MemoryContextSwitchTo(oldcontext); } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 328aea5f07..d3ccf0dd90 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.61 2002/12/05 15:50:32 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.62 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -284,7 +284,8 @@ postquel_end(execution_state *es) if (es->qd->operation != CMD_UTILITY) ExecutorEnd(es->qd); - pfree(es->qd); + FreeQueryDesc(es->qd); + es->qd = NULL; es->status = F_EXEC_DONE; diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index e6ba388763..769e88a839 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -45,7 +45,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.100 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.101 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1392,8 +1392,6 @@ ExecEndAgg(AggState *node) tuplesort_end(peraggstate->sortstate); } - ExecFreeProjectionInfo(&node->ss.ps); - /* * Free both the expr contexts. */ @@ -1401,18 +1399,13 @@ ExecEndAgg(AggState *node) node->ss.ps.ps_ExprContext = node->tmpcontext; ExecFreeExprContext(&node->ss.ps); + /* clean up tuple table */ + ExecClearTuple(node->ss.ss_ScanTupleSlot); + MemoryContextDelete(node->aggcontext); outerPlan = outerPlanState(node); ExecEndNode(outerPlan); - - /* clean up tuple table */ - ExecClearTuple(node->ss.ss_ScanTupleSlot); - if (node->grp_firstTuple != NULL) - { - heap_freetuple(node->grp_firstTuple); - node->grp_firstTuple = NULL; - } } void diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index d0bf78631d..78f66523d5 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.15 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeFunctionscan.c,v 1.16 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -273,9 +273,8 @@ void ExecEndFunctionScan(FunctionScanState *node) { /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 8bb72ba343..58f6c1b34e 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -15,7 +15,7 @@ * locate group boundaries. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.52 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.53 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -218,19 +218,13 @@ ExecEndGroup(GroupState *node) { PlanState *outerPlan; - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); - outerPlan = outerPlanState(node); - ExecEndNode(outerPlan); - /* clean up tuple table */ ExecClearTuple(node->ss.ss_ScanTupleSlot); - if (node->grp_firstTuple != NULL) - { - heap_freetuple(node->grp_firstTuple); - node->grp_firstTuple = NULL; - } + + outerPlan = outerPlanState(node); + ExecEndNode(outerPlan); } void diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 45ba826317..efdd3b3cab 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.70 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHash.c,v 1.71 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -179,10 +179,8 @@ ExecEndHash(HashState *node) PlanState *outerPlan; /* - * free projection info. no need to free result type info because - * that came from the outer plan... + * free exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); /* diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 07de870381..8f899b577d 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.44 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.45 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -456,17 +456,10 @@ ExecEndHashJoin(HashJoinState *node) } /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); - /* - * clean up subtrees - */ - ExecEndNode(outerPlanState(node)); - ExecEndNode(innerPlanState(node)); - /* * clean out the tuple table */ @@ -474,6 +467,11 @@ ExecEndHashJoin(HashJoinState *node) ExecClearTuple(node->hj_OuterTupleSlot); ExecClearTuple(node->hj_HashTupleSlot); + /* + * clean up subtrees + */ + ExecEndNode(outerPlanState(node)); + ExecEndNode(innerPlanState(node)); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 1e36e93113..7b96723844 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.74 2002/12/13 19:45:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.75 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -399,44 +399,38 @@ ExecIndexReScan(IndexScanState *node, ExprContext *exprCtxt) /* ---------------------------------------------------------------- * ExecEndIndexScan - * - * old comments - * Releases any storage allocated through C routines. - * Returns nothing. * ---------------------------------------------------------------- */ void ExecEndIndexScan(IndexScanState *node) { - ExprState ***runtimeKeyInfo; - ScanKey *scanKeys; - int *numScanKeys; int numIndices; - Relation relation; RelationPtr indexRelationDescs; IndexScanDescPtr indexScanDescs; + Relation relation; int i; - runtimeKeyInfo = node->iss_RuntimeKeyInfo; - /* * extract information from the node */ numIndices = node->iss_NumIndices; - scanKeys = node->iss_ScanKeys; - numScanKeys = node->iss_NumScanKeys; indexRelationDescs = node->iss_RelationDescs; indexScanDescs = node->iss_ScanDescs; relation = node->ss.ss_currentRelation; /* - * Free the projection info and the scan attribute info + * Free the exprcontext(s) */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); if (node->iss_RuntimeContext) FreeExprContext(node->iss_RuntimeContext); + /* + * clear out tuple table slots + */ + ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); + ExecClearTuple(node->ss.ss_ScanTupleSlot); + /* * close the index relations */ @@ -458,36 +452,6 @@ ExecEndIndexScan(IndexScanState *node) * locking, however.) */ heap_close(relation, NoLock); - - /* - * free the scan keys used in scanning the indices - */ - for (i = 0; i < numIndices; i++) - { - if (scanKeys[i] != NULL) - pfree(scanKeys[i]); - } - pfree(scanKeys); - pfree(numScanKeys); - - if (runtimeKeyInfo) - { - for (i = 0; i < numIndices; i++) - { - if (runtimeKeyInfo[i] != NULL) - pfree(runtimeKeyInfo[i]); - } - pfree(runtimeKeyInfo); - } - - /* - * clear out tuple table slots - */ - ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); - ExecClearTuple(node->ss.ss_ScanTupleSlot); - pfree(node->iss_RelationDescs); - pfree(node->iss_ScanDescs); - pfree(node); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c index 6abd83de8a..39d09331ce 100644 --- a/src/backend/executor/nodeLimit.c +++ b/src/backend/executor/nodeLimit.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.13 2002/12/13 19:45:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeLimit.c,v 1.14 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -349,10 +349,10 @@ ExecEndLimit(LimitState *node) { ExecFreeExprContext(&node->ps); - ExecEndNode(outerPlanState(node)); - /* clean up tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index cf7ca89f4a..a1725901a7 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.39 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.40 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -196,17 +196,17 @@ ExecEndMaterial(MaterialState *node) */ ExecClearTuple(node->ss.ss_ScanTupleSlot); - /* - * shut down the subplan - */ - ExecEndNode(outerPlanState(node)); - /* * Release tuplestore resources */ if (node->tuplestorestate != NULL) tuplestore_end((Tuplestorestate *) node->tuplestorestate); node->tuplestorestate = NULL; + + /* + * shut down the subplan + */ + ExecEndNode(outerPlanState(node)); } /* ---------------------------------------------------------------- diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 1bb5878d81..af6cd8d6f3 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.54 2002/12/13 19:45:54 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.55 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1551,23 +1551,22 @@ ExecEndMergeJoin(MergeJoinState *node) "ending node processing"); /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); - /* - * shut down the subplans - */ - ExecEndNode(innerPlanState(node)); - ExecEndNode(outerPlanState(node)); - /* * clean out the tuple table */ ExecClearTuple(node->js.ps.ps_ResultTupleSlot); ExecClearTuple(node->mj_MarkedTupleSlot); + /* + * shut down the subplans + */ + ExecEndNode(innerPlanState(node)); + ExecEndNode(outerPlanState(node)); + MJ1_printf("ExecEndMergeJoin: %s\n", "node processing ended"); } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 452ed7d70c..917a7011cb 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.28 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.29 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -364,22 +364,21 @@ ExecEndNestLoop(NestLoopState *node) "ending node processing"); /* - * Free the projection info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->js.ps); ExecFreeExprContext(&node->js.ps); + /* + * clean out the tuple table + */ + ExecClearTuple(node->js.ps.ps_ResultTupleSlot); + /* * close down subplans */ ExecEndNode(outerPlanState(node)); ExecEndNode(innerPlanState(node)); - /* - * clean out the tuple table - */ - ExecClearTuple(node->js.ps.ps_ResultTupleSlot); - NL1_printf("ExecEndNestLoop: %s\n", "node processing ended"); } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 3f2c9927e0..9ea75eb3ce 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -34,7 +34,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.23 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.24 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -248,9 +248,8 @@ void ExecEndResult(ResultState *node) { /* - * Free the projection info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); /* diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 6a7393795b..6628a9eecb 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.40 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.41 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -264,22 +264,21 @@ ExecEndSeqScan(SeqScanState *node) scanDesc = node->ss_currentScanDesc; /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ps); ExecFreeExprContext(&node->ps); - /* - * close heap scan - */ - heap_endscan(scanDesc); - /* * clean out the tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); ExecClearTuple(node->ss_ScanTupleSlot); + /* + * close heap scan + */ + heap_endscan(scanDesc); + /* * close the heap relation. * diff --git a/src/backend/executor/nodeSetOp.c b/src/backend/executor/nodeSetOp.c index a81a4a29d9..965a2a6466 100644 --- a/src/backend/executor/nodeSetOp.c +++ b/src/backend/executor/nodeSetOp.c @@ -21,7 +21,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.7 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSetOp.c,v 1.8 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -301,9 +301,9 @@ ExecEndSetOp(SetOpState *node) ExecClearTuple(node->ps.ps_ResultTupleSlot); node->ps.ps_OuterTupleSlot = NULL; - ExecEndNode(outerPlanState(node)); - MemoryContextDelete(node->tempContext); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index e1b4db7a51..a37583241f 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.41 2002/12/05 15:50:33 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.42 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -284,11 +284,6 @@ ExecEndSort(SortState *node) */ ExecClearTuple(node->ss.ss_ScanTupleSlot); - /* - * shut down the subplan - */ - ExecEndNode(outerPlanState(node)); - /* * Release tuplesort resources */ @@ -296,6 +291,11 @@ ExecEndSort(SortState *node) tuplesort_end((Tuplesortstate *) node->tuplesortstate); node->tuplesortstate = NULL; + /* + * shut down the subplan + */ + ExecEndNode(outerPlanState(node)); + SO1_printf("ExecEndSort: %s\n", "sort node shutdown"); } diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 3a2ca974ae..2f5ab52e21 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.38 2002/12/14 00:17:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.39 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -47,9 +47,10 @@ ExecSubPlan(SubPlanState *node, /* * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. + * Switch to the child plan's per-query context for manipulating its + * chgParam, calling ExecProcNode on it, etc. */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); if (subplan->setParam != NIL) elog(ERROR, "ExecSubPlan: can't set parent params from subquery"); @@ -132,10 +133,13 @@ ExecSubPlan(SubPlanState *node, * ExecProcNode() call. node->curTuple keeps track of the * copied tuple for eventual freeing. */ + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tup = heap_copytuple(tup); if (node->curTuple) heap_freetuple(node->curTuple); node->curTuple = tup; + MemoryContextSwitchTo(node->sub_estate->es_query_cxt); + result = heap_getattr(tup, col, tdesc, isNull); /* keep scanning subplan to make sure there's only one tuple */ continue; @@ -295,6 +299,7 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) { SubPlan *subplan = (SubPlan *) node->xprstate.expr; EState *sp_estate; + MemoryContext oldcontext; /* * Do access checking on the rangetable entries in the subquery. @@ -303,15 +308,23 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) ExecCheckRTPerms(subplan->rtable, CMD_SELECT); /* - * initialize state + * initialize my state */ node->needShutdown = false; node->curTuple = NULL; /* * create an EState for the subplan + * + * The subquery needs its own EState because it has its own rangetable. + * It shares our Param ID space, however. XXX if rangetable access were + * done differently, the subquery could share our EState, which would + * eliminate some thrashing about in this module... */ sp_estate = CreateExecutorState(); + node->sub_estate = sp_estate; + + oldcontext = MemoryContextSwitchTo(sp_estate->es_query_cxt); sp_estate->es_range_table = subplan->rtable; sp_estate->es_param_list_info = estate->es_param_list_info; @@ -322,12 +335,14 @@ ExecInitSubPlan(SubPlanState *node, EState *estate) sp_estate->es_instrument = estate->es_instrument; /* - * Start up the subplan + * Start up the subplan (this is a very cut-down form of InitPlan()) */ node->planstate = ExecInitNode(subplan->plan, sp_estate); node->needShutdown = true; /* now we need to shutdown the subplan */ + MemoryContextSwitchTo(oldcontext); + /* * If this plan is un-correlated or undirect correlated one and want * to set params for parent plan then prepare parameters. @@ -376,10 +391,9 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) bool found = false; /* - * We are probably in a short-lived expression-evaluation context. - * Switch to longer-lived per-query context. + * Must switch to child query's per-query memory context. */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); if (subLinkType == ANY_SUBLINK || subLinkType == ALL_SUBLINK) @@ -415,15 +429,18 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) found = true; /* - * We need to copy the subplan's tuple in case any of the params - * are pass-by-ref type --- the pointers stored in the param - * structs will point at this copied tuple! node->curTuple keeps - * track of the copied tuple for eventual freeing. + * We need to copy the subplan's tuple into our own context, + * in case any of the params are pass-by-ref type --- the pointers + * stored in the param structs will point at this copied tuple! + * node->curTuple keeps track of the copied tuple for eventual + * freeing. */ + MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tup = heap_copytuple(tup); if (node->curTuple) heap_freetuple(node->curTuple); node->curTuple = tup; + MemoryContextSwitchTo(node->sub_estate->es_query_cxt); foreach(lst, subplan->setParam) { @@ -460,7 +477,10 @@ ExecSetParamPlan(SubPlanState *node, ExprContext *econtext) if (planstate->plan->extParam == NULL) /* un-correlated ... */ { - ExecEndNode(planstate); + ExecEndPlan(planstate, node->sub_estate); + /* mustn't free context while still in it... */ + MemoryContextSwitchTo(oldcontext); + FreeExecutorState(node->sub_estate); node->needShutdown = false; } @@ -476,7 +496,12 @@ ExecEndSubPlan(SubPlanState *node) { if (node->needShutdown) { - ExecEndNode(node->planstate); + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(node->sub_estate->es_query_cxt); + ExecEndPlan(node->planstate, node->sub_estate); + MemoryContextSwitchTo(oldcontext); + FreeExecutorState(node->sub_estate); node->needShutdown = false; } if (node->curTuple) diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 68291ba6e3..4466ef3421 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -12,7 +12,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.15 2002/12/13 19:45:55 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubqueryscan.c,v 1.16 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,7 @@ SubqueryNext(SubqueryScanState *node) EState *estate; ScanDirection direction; TupleTableSlot *slot; + MemoryContext oldcontext; /* * get information from the estate and scan state @@ -66,12 +67,17 @@ SubqueryNext(SubqueryScanState *node) */ /* - * get the next tuple from the sub-query + * Get the next tuple from the sub-query. We have to be careful to + * run it in its appropriate memory context. */ node->sss_SubEState->es_direction = direction; + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); + slot = ExecProcNode(node->subplan); + MemoryContextSwitchTo(oldcontext); + node->ss.ss_ScanTupleSlot = slot; return slot; @@ -106,6 +112,7 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) SubqueryScanState *subquerystate; RangeTblEntry *rte; EState *sp_estate; + MemoryContext oldcontext; /* * SubqueryScan should not have any "normal" children. @@ -152,9 +159,17 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) rte = rt_fetch(node->scan.scanrelid, estate->es_range_table); Assert(rte->rtekind == RTE_SUBQUERY); + /* + * The subquery needs its own EState because it has its own rangetable. + * It shares our Param ID space, however. XXX if rangetable access were + * done differently, the subquery could share our EState, which would + * eliminate some thrashing about in this module... + */ sp_estate = CreateExecutorState(); subquerystate->sss_SubEState = sp_estate; + oldcontext = MemoryContextSwitchTo(sp_estate->es_query_cxt); + sp_estate->es_range_table = rte->subquery->rtable; sp_estate->es_param_list_info = estate->es_param_list_info; sp_estate->es_param_exec_vals = estate->es_param_exec_vals; @@ -163,8 +178,13 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate) sp_estate->es_snapshot = estate->es_snapshot; sp_estate->es_instrument = estate->es_instrument; + /* + * Start up the subplan (this is a very cut-down form of InitPlan()) + */ subquerystate->subplan = ExecInitNode(node->subplan, sp_estate); + MemoryContextSwitchTo(oldcontext); + subquerystate->ss.ss_ScanTupleSlot = NULL; subquerystate->ss.ps.ps_TupFromTlist = false; @@ -197,10 +217,11 @@ ExecCountSlotsSubqueryScan(SubqueryScan *node) void ExecEndSubqueryScan(SubqueryScanState *node) { + MemoryContext oldcontext; + /* - * Free the projection info and the scan attribute info + * Free the exprcontext */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* @@ -211,15 +232,13 @@ ExecEndSubqueryScan(SubqueryScanState *node) /* * close down subquery */ - ExecEndNode(node->subplan); + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); - /* - * clean up subquery's tuple table - */ - node->ss.ss_ScanTupleSlot = NULL; - ExecDropTupleTable(node->sss_SubEState->es_tupleTable, true); + ExecEndPlan(node->subplan, node->sss_SubEState); - /* XXX we seem to be leaking the sub-EState... */ + MemoryContextSwitchTo(oldcontext); + + FreeExecutorState(node->sss_SubEState); } /* ---------------------------------------------------------------- @@ -232,12 +251,17 @@ void ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt) { EState *estate; + MemoryContext oldcontext; estate = node->ss.ps.state; + oldcontext = MemoryContextSwitchTo(node->sss_SubEState->es_query_cxt); + /* * ExecReScan doesn't know about my subplan, so I have to do - * changed-parameter signaling myself. + * changed-parameter signaling myself. This is just as well, + * because the subplan has its own memory context in which its + * chgParam lists live. */ if (node->ss.ps.chgParam != NULL) SetChangedParamList(node->subplan, node->ss.ps.chgParam); @@ -249,5 +273,7 @@ ExecSubqueryReScan(SubqueryScanState *node, ExprContext *exprCtxt) if (node->subplan->chgParam == NULL) ExecReScan(node->subplan, NULL); + MemoryContextSwitchTo(oldcontext); + node->ss.ss_ScanTupleSlot = NULL; } diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index ba2793407c..7e35bc07cd 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.29 2002/12/13 19:45:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.30 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -278,19 +278,8 @@ void ExecEndTidScan(TidScanState *node) { /* - * extract information from the node + * Free the exprcontext */ - if (node && node->tss_TidList) - pfree(node->tss_TidList); - - /* - * Free the projection info and the scan attribute info - * - * Note: we don't ExecFreeResultType(scanstate) because the rule manager - * depends on the tupType returned by ExecMain(). So for now, this is - * freed at end-transaction time. -cim 6/2/91 - */ - ExecFreeProjectionInfo(&node->ss.ps); ExecFreeExprContext(&node->ss.ps); /* diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index 88b08061f9..415594f92c 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.35 2002/12/05 15:50:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.36 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -194,15 +194,10 @@ ExecEndUnique(UniqueState *node) { /* clean up tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); - if (node->priorTuple != NULL) - { - heap_freetuple(node->priorTuple); - node->priorTuple = NULL; - } - - ExecEndNode(outerPlanState(node)); MemoryContextDelete(node->tempContext); + + ExecEndNode(outerPlanState(node)); } diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 147becf6ed..cde9ab6ff6 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.79 2002/12/05 15:50:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/spi.c,v 1.80 2002/12/15 16:17:46 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1287,23 +1287,23 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit, int tcount) elog(FATAL, "SPI_select: # of processed tuples check failed"); } - ExecutorEnd(queryDesc); - -#ifdef SPI_EXECUTOR_STATS - if (ShowExecutorStats) - ShowUsage("SPI EXECUTOR STATS"); -#endif - if (dest == SPI) { SPI_processed = _SPI_current->processed; SPI_lastoid = save_lastoid; SPI_tuptable = _SPI_current->tuptable; } - queryDesc->dest = dest; + + ExecutorEnd(queryDesc); + + FreeQueryDesc(queryDesc); + +#ifdef SPI_EXECUTOR_STATS + if (ShowExecutorStats) + ShowUsage("SPI EXECUTOR STATS"); +#endif return res; - } /* diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index e4eedd1179..984c930e3a 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.128 2002/12/13 19:45:56 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.129 2002/12/15 16:17:49 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1132,7 +1132,8 @@ pred_test_simple_clause(Expr *predicate, Node *clause) HeapTuple tuple; ScanKeyData entry[1]; Form_pg_amop aform; - ExprContext *econtext; + EState *estate; + MemoryContext oldcontext; /* First try the equal() test */ if (equal((Node *) predicate, clause)) @@ -1267,20 +1268,33 @@ pred_test_simple_clause(Expr *predicate, Node *clause) ReleaseSysCache(tuple); /* - * 5. Evaluate the test + * 5. Evaluate the test. For this we need an EState. */ + estate = CreateExecutorState(); + + /* We can use the estate's working context to avoid memory leaks. */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* Build expression tree */ test_expr = make_opclause(test_op, BOOLOID, false, (Expr *) clause_const, (Expr *) pred_const); - set_opfuncid((OpExpr *) test_expr); - test_exprstate = ExecInitExpr(test_expr, NULL); - econtext = MakeExprContext(NULL, CurrentMemoryContext); - test_result = ExecEvalExprSwitchContext(test_exprstate, econtext, + /* Prepare it for execution */ + test_exprstate = ExecPrepareExpr(test_expr, estate); + + /* And execute it. */ + test_result = ExecEvalExprSwitchContext(test_exprstate, + GetPerTupleExprContext(estate), &isNull, NULL); - FreeExprContext(econtext); + + /* Get back to outer memory context */ + MemoryContextSwitchTo(oldcontext); + + /* Release all the junk we just created */ + FreeExecutorState(estate); if (isNull) { diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 1d87afdc42..4c87a95c3b 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.119 2002/12/14 00:17:59 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.120 2002/12/15 16:17:50 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1684,7 +1684,8 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple) bool has_null_input = false; FuncExpr *newexpr; ExprState *newexprstate; - ExprContext *econtext; + EState *estate; + MemoryContext oldcontext; Datum const_val; bool const_is_null; List *arg; @@ -1729,7 +1730,14 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple) * * We use the executor's routine ExecEvalExpr() to avoid duplication of * code and ensure we get the same result as the executor would get. - * + * To use the executor, we need an EState. + */ + estate = CreateExecutorState(); + + /* We can use the estate's working context to avoid memory leaks. */ + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + /* * Build a new FuncExpr node containing the already-simplified arguments. */ newexpr = makeNode(FuncExpr); @@ -1739,27 +1747,35 @@ evaluate_function(Oid funcid, List *args, HeapTuple func_tuple) newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ newexpr->args = args; - /* Get info needed about result datatype */ - get_typlenbyval(result_typeid, &resultTypLen, &resultTypByVal); + /* + * Prepare it for execution. + */ + newexprstate = ExecPrepareExpr((Expr *) newexpr, estate); /* - * It is OK to use a dummy econtext because none of the + * And evaluate it. + * + * It is OK to use a default econtext because none of the * ExecEvalExpr() code used in this situation will use econtext. That * might seem fortuitous, but it's not so unreasonable --- a constant * expression does not depend on context, by definition, n'est ce pas? */ - econtext = MakeExprContext(NULL, CurrentMemoryContext); - - newexprstate = ExecInitExpr((Expr *) newexpr, NULL); - - const_val = ExecEvalExprSwitchContext(newexprstate, econtext, + const_val = ExecEvalExprSwitchContext(newexprstate, + GetPerTupleExprContext(estate), &const_is_null, NULL); + /* Get info needed about result datatype */ + get_typlenbyval(result_typeid, &resultTypLen, &resultTypByVal); + + /* Get back to outer memory context */ + MemoryContextSwitchTo(oldcontext); + /* Must copy result out of sub-context used by expression eval */ if (!const_is_null) const_val = datumCopy(const_val, resultTypByVal, resultTypLen); - FreeExprContext(econtext); + /* Release all the junk we just created */ + FreeExecutorState(estate); /* * Make the constant result node. diff --git a/src/backend/tcop/pquery.c b/src/backend/tcop/pquery.c index 24d4aac8b2..1e02e42193 100644 --- a/src/backend/tcop/pquery.c +++ b/src/backend/tcop/pquery.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.57 2002/12/05 15:50:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/pquery.c,v 1.58 2002/12/15 16:17:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -52,6 +52,18 @@ CreateQueryDesc(Query *parsetree, return qd; } +/* + * FreeQueryDesc + */ +void +FreeQueryDesc(QueryDesc *qdesc) +{ + /* Can't be a live query */ + Assert(qdesc->estate == NULL); + /* Only the QueryDesc itself need be freed */ + pfree(qdesc); +} + /* ---------------- * PreparePortal * ---------------- @@ -152,9 +164,8 @@ ProcessQuery(Query *parsetree, * QueryDesc */ /* - * We stay in portal's memory context for now, so that query desc, - * exec state, and plan startup info are also allocated in the portal - * context. + * We stay in portal's memory context for now, so that query desc + * is also allocated in the portal context. */ } @@ -231,4 +242,6 @@ ProcessQuery(Query *parsetree, * Now, we close down all the scans and free allocated resources. */ ExecutorEnd(queryDesc); + + FreeQueryDesc(queryDesc); } diff --git a/src/include/executor/execdesc.h b/src/include/executor/execdesc.h index 9a95551d7c..27148bb1d8 100644 --- a/src/include/executor/execdesc.h +++ b/src/include/executor/execdesc.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execdesc.h,v 1.21 2002/12/05 15:50:36 tgl Exp $ + * $Id: execdesc.h,v 1.22 2002/12/15 16:17:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,4 +50,6 @@ extern QueryDesc *CreateQueryDesc(Query *parsetree, Plan *plantree, ParamListInfo params, bool doInstrument); +extern void FreeQueryDesc(QueryDesc *qdesc); + #endif /* EXECDESC_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 9b7af6d36f..571f35c64e 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.83 2002/12/14 00:17:59 tgl Exp $ + * $Id: executor.h,v 1.84 2002/12/15 16:17:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -54,8 +54,8 @@ extern void ExecutorStart(QueryDesc *queryDesc); extern TupleTableSlot *ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count); extern void ExecutorEnd(QueryDesc *queryDesc); -extern EState *CreateExecutorState(void); extern void ExecCheckRTPerms(List *rangeTable, CmdType operation); +extern void ExecEndPlan(PlanState *planstate, EState *estate); extern void ExecConstraints(const char *caller, ResultRelInfo *resultRelInfo, TupleTableSlot *slot, EState *estate); extern TupleTableSlot *EvalPlanQual(EState *estate, Index rti, @@ -93,6 +93,7 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern SubPlanState *ExecInitExprInitPlan(SubPlan *node, PlanState *parent); +extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); @@ -157,23 +158,9 @@ extern void end_tup_output(TupOutputState *tstate); /* * prototypes from functions in execUtils.c */ -extern void ResetTupleCount(void); -extern void ExecAssignExprContext(EState *estate, PlanState *planstate); -extern void ExecAssignResultType(PlanState *planstate, - TupleDesc tupDesc, bool shouldFree); -extern void ExecAssignResultTypeFromOuterPlan(PlanState *planstate); -extern void ExecAssignResultTypeFromTL(PlanState *planstate); -extern TupleDesc ExecGetResultType(PlanState *planstate); -extern void ExecAssignProjectionInfo(PlanState *planstate); -extern void ExecFreeProjectionInfo(PlanState *planstate); -extern void ExecFreeExprContext(PlanState *planstate); -extern TupleDesc ExecGetScanType(ScanState *scanstate); -extern void ExecAssignScanType(ScanState *scanstate, - TupleDesc tupDesc, bool shouldFree); -extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate); - -extern ExprContext *MakeExprContext(TupleTableSlot *slot, - MemoryContext queryContext); +extern EState *CreateExecutorState(void); +extern void FreeExecutorState(EState *estate); +extern ExprContext *CreateExprContext(EState *estate); extern void FreeExprContext(ExprContext *econtext); #define ResetExprContext(econtext) \ @@ -197,6 +184,19 @@ extern ExprContext *MakePerTupleExprContext(EState *estate); ResetExprContext((estate)->es_per_tuple_exprcontext); \ } while (0) +extern void ExecAssignExprContext(EState *estate, PlanState *planstate); +extern void ExecAssignResultType(PlanState *planstate, + TupleDesc tupDesc, bool shouldFree); +extern void ExecAssignResultTypeFromOuterPlan(PlanState *planstate); +extern void ExecAssignResultTypeFromTL(PlanState *planstate); +extern TupleDesc ExecGetResultType(PlanState *planstate); +extern void ExecAssignProjectionInfo(PlanState *planstate); +extern void ExecFreeExprContext(PlanState *planstate); +extern TupleDesc ExecGetScanType(ScanState *scanstate); +extern void ExecAssignScanType(ScanState *scanstate, + TupleDesc tupDesc, bool shouldFree); +extern void ExecAssignScanTypeFromOuterPlan(ScanState *scanstate); + extern void ExecOpenIndices(ResultRelInfo *resultRelInfo); extern void ExecCloseIndices(ResultRelInfo *resultRelInfo); extern void ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index f8e1f7cc4c..65c5f23884 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.85 2002/12/14 00:17:59 tgl Exp $ + * $Id: execnodes.h,v 1.86 2002/12/15 16:17:56 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -81,14 +81,14 @@ typedef struct ExprContext_CB * context. * * There are two memory contexts associated with an ExprContext: - * * ecxt_per_query_memory is a relatively long-lived context (such as - * TransactionCommandContext); typically it's the same context the - * ExprContext node itself is allocated in. This context can be - * used for purposes such as storing function call cache info. + * * ecxt_per_query_memory is a query-lifespan context, typically the same + * context the ExprContext node itself is allocated in. This context + * can be used for purposes such as storing function call cache info. * * ecxt_per_tuple_memory is a short-term context for expression results. * As the name suggests, it will typically be reset once per tuple, * before we begin to evaluate expressions for that tuple. Each * ExprContext normally has its very own per-tuple memory context. + * * CurrentMemoryContext should be set to ecxt_per_tuple_memory before * calling ExecEvalExpr() --- see ExecEvalExprSwitchContext(). * ---------------- @@ -118,6 +118,9 @@ typedef struct ExprContext Datum domainValue_datum; bool domainValue_isNull; + /* Link to containing EState */ + struct EState *ecxt_estate; + /* Functions to call back when ExprContext is shut down */ ExprContext_CB *ecxt_callbacks; } ExprContext; @@ -277,45 +280,43 @@ typedef struct ResultRelInfo /* ---------------- * EState information * - * direction direction of the scan - * - * snapshot time qual to use - * - * range_table array of scan relation information - * - * result_relation information for insert/update/delete queries - * - * into_relation_descriptor relation being retrieved "into" - * - * param_list_info information about Param values - * - * tupleTable this is a pointer to an array - * of pointers to tuples used by - * the executor at any given moment. + * Master working state for an Executor invocation * ---------------- */ typedef struct EState { NodeTag type; - ScanDirection es_direction; - Snapshot es_snapshot; - List *es_range_table; + + /* Basic state for all query types: */ + ScanDirection es_direction; /* current scan direction */ + Snapshot es_snapshot; /* time qual to use */ + List *es_range_table; /* List of RangeTableEntrys */ + + /* Info about target table for insert/update/delete queries: */ ResultRelInfo *es_result_relations; /* array of ResultRelInfos */ int es_num_result_relations; /* length of array */ ResultRelInfo *es_result_relation_info; /* currently active array * elt */ JunkFilter *es_junkFilter; /* currently active junk filter */ - Relation es_into_relation_descriptor; + Relation es_into_relation_descriptor; /* for SELECT INTO */ + + /* Parameter info: */ ParamListInfo es_param_list_info; /* values of external params */ ParamExecData *es_param_exec_vals; /* values of internal params */ - TupleTable es_tupleTable; + + /* Other working state: */ + MemoryContext es_query_cxt; /* per-query context in which EState lives */ + + TupleTable es_tupleTable; /* Array of TupleTableSlots */ + 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 */ - MemoryContext es_query_cxt; /* per-query context in which EState lives */ bool es_instrument; /* true requests runtime instrumentation */ + List *es_exprcontexts; /* List of ExprContexts within EState */ + /* * this ExprContext is for per-output-tuple operations, such as * constraint checks and index-value computations. It will be reset @@ -457,6 +458,7 @@ typedef struct BoolExprState typedef struct SubPlanState { ExprState xprstate; + EState *sub_estate; /* subselect plan has its own EState */ struct PlanState *planstate; /* subselect plan's state tree */ bool needShutdown; /* TRUE = need to shutdown subplan */ HeapTuple curTuple; /* copy of most recent tuple from subplan */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 8908a43a78..a569d9ee9d 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.74 2002/12/13 19:46:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.75 2002/12/15 16:17:58 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -3227,7 +3227,6 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, bool *isNull, Oid *rettype) { - _SPI_plan *spi_plan = (_SPI_plan *) expr->plan; Datum retval; PLpgSQL_var *var; PLpgSQL_rec *rec; @@ -3242,14 +3241,11 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, ParamListInfo paramLI; /* - * Create a simple expression context to hold the arguments. - * - * NOTE: we pass the SPI plan's context as the query-lifetime context for - * function cache nodes and suchlike allocations. This is appropriate - * because that's where the expression tree itself is, and the - * function cache nodes must live as long as it does. + * Create an expression context to hold the arguments and the result + * of this expression evaluation. This must be a child of the EState + * we created in the SPI plan's context. */ - econtext = MakeExprContext(NULL, spi_plan->plancxt); + econtext = CreateExprContext(expr->plan_simple_estate); /* * Param list can live in econtext's temporary memory context. @@ -3691,13 +3687,20 @@ exec_simple_check_plan(PLpgSQL_expr * expr) return; /* - * Yes - this is a simple expression. Prepare to execute it, and - * stash away the result type. Put the expression state tree in the - * plan context so it will have appropriate lifespan. + * Yes - this is a simple expression. Prepare to execute it. + * We need an EState and an expression state tree, which we'll put + * into the plan context so they will have appropriate lifespan. */ oldcontext = MemoryContextSwitchTo(spi_plan->plancxt); - expr->plan_simple_expr = ExecInitExpr(tle->expr, NULL); + + expr->plan_simple_estate = CreateExecutorState(); + + expr->plan_simple_expr = ExecPrepareExpr(tle->expr, + expr->plan_simple_estate); + MemoryContextSwitchTo(oldcontext); + + /* Also stash away the expression result type */ expr->plan_simple_type = exprType((Node *) tle->expr); } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 945569b6f6..f051c01572 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.30 2002/12/13 19:46:01 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.31 2002/12/15 16:17:59 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -166,6 +166,7 @@ typedef struct char *query; void *plan; ExprState *plan_simple_expr; + EState *plan_simple_estate; Oid plan_simple_type; Oid *plan_argtypes; int nparams;