1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-11 20:28:21 +03:00

First stage of reclaiming memory in executor by resetting short-term

memory contexts.  Currently, only leaks in expressions executed as
quals or projections are handled.  Clean up some old dead cruft in
executor while at it --- unused fields in state nodes, that sort of thing.
This commit is contained in:
Tom Lane
2000-07-12 02:37:39 +00:00
parent 46fb9c29e2
commit badce86a2c
53 changed files with 1536 additions and 1584 deletions

View File

@ -8,15 +8,16 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.72 2000/06/15 04:09:50 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.73 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecEvalExpr - evaluate an expression and return a datum
* ExecEvalExprSwitchContext - same, but switch into eval memory context
* ExecQual - return true/false if qualification is satisfied
* ExecTargetList - form a new tuple by projecting the given tuple
* ExecProject - form a new tuple by projecting the given tuple
*
* NOTES
* ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
@ -24,7 +25,7 @@
* implemented recursively. Eliminating the recursion is bound to
* improve the speed of the executor.
*
* ExecTargetList() is used to make tuple projections. Rather then
* ExecProject() is used to make tuple projections. Rather then
* trying to speed it up, the execution plan should be pre-processed
* to facilitate attribute sharing between nodes wherever possible,
* instead of doing needless copying. -cim 5/31/91
@ -44,31 +45,19 @@
#include "utils/fcache2.h"
/*
* externs and constants
*/
/*
* XXX Used so we can get rid of use of Const nodes in the executor.
* Currently only used by ExecHashGetBucket and set only by ExecMakeVarConst
* and by ExecEvalArrayRef.
*/
bool execConstByVal;
int execConstLen;
/* static functions decls */
/* static function decls */
static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull);
static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext,
bool *isNull, bool *isDone);
static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext,
bool *isNull, bool *isDone);
static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext,
List *argList, FunctionCallInfo fcinfo,
bool *argIsDone);
static Datum ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull);
static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull);
static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull);
static Datum ExecMakeFunctionResult(Node *node, List *arguments,
@ -100,10 +89,11 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
if (arrayRef->refexpr != NULL)
{
array_scanner = (ArrayType *) ExecEvalExpr(arrayRef->refexpr,
econtext,
isNull,
isDone);
array_scanner = (ArrayType *)
DatumGetPointer(ExecEvalExpr(arrayRef->refexpr,
econtext,
isNull,
isDone));
/* If refexpr yields NULL, result is always NULL, for now anyway */
if (*isNull)
return (Datum) NULL;
@ -128,10 +118,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
upper.indx[i++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
&dummy);
upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
&dummy));
/* If any index expr yields NULL, result is NULL */
if (*isNull)
return (Datum) NULL;
@ -145,10 +135,10 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
lower.indx[j++] = (int32) ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
&dummy);
lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt),
econtext,
isNull,
&dummy));
/* If any index expr yields NULL, result is NULL */
if (*isNull)
return (Datum) NULL;
@ -171,9 +161,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
if (*isNull)
return (Datum) NULL;
execConstByVal = arrayRef->refelembyval;
execConstLen = arrayRef->refelemlength;
if (array_scanner == NULL)
return sourceData; /* XXX do something else? */
@ -199,9 +186,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef,
isNull));
}
execConstByVal = arrayRef->refelembyval;
execConstLen = arrayRef->refelemlength;
if (lIndex == NULL)
return array_ref(array_scanner, i,
upper.indx,
@ -325,7 +309,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
ExecSetSlotDescriptor(tempSlot, td);
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
return (Datum) tempSlot;
return PointerGetDatum(tempSlot);
}
result = heap_getattr(heapTuple, /* tuple containing attribute */
@ -338,7 +322,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
* return null if att is null
*/
if (*isNull)
return (Datum) NULL;
return (Datum) 0;
/*
* get length and type information.. ??? what should we do about
@ -364,9 +348,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
byval = tuple_type->attrs[attnum - 1]->attbyval ? true : false;
}
execConstByVal = byval;
execConstLen = len;
return result;
}
@ -397,7 +378,6 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
Datum
ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
{
char *thisParameterName;
int thisParameterKind = expression->paramkind;
AttrNumber thisParameterId = expression->paramid;
@ -406,11 +386,15 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
if (thisParameterKind == PARAM_EXEC)
{
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
ParamExecData *prm;
prm = &(econtext->ecxt_param_exec_vals[thisParameterId]);
if (prm->execPlan != NULL)
ExecSetParamPlan(prm->execPlan);
Assert(prm->execPlan == NULL);
{
ExecSetParamPlan(prm->execPlan, econtext);
/* ExecSetParamPlan should have processed this param... */
Assert(prm->execPlan == NULL);
}
*isNull = prm->isnull;
return prm->value;
}
@ -493,7 +477,7 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
if (paramList->isnull)
{
*isNull = true;
return (Datum) NULL;
return (Datum) 0;
}
if (expression->param_tlist != NIL)
@ -526,8 +510,12 @@ ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull)
* named attribute out of the tuple from the arg slot. User defined
* C functions which take a tuple as an argument are expected
* to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
*
* XXX these two functions are misdeclared: they should be declared to
* return Datum. They are not used anywhere in the backend proper, and
* exist only for use by user-defined functions. Should we change their
* definitions, at risk of breaking user code?
*/
/* static but gets called from external functions */
char *
GetAttributeByNum(TupleTableSlot *slot,
AttrNumber attrno,
@ -559,18 +547,6 @@ GetAttributeByNum(TupleTableSlot *slot,
return (char *) retval;
}
/* XXX name for catalogs */
#ifdef NOT_USED
char *
att_by_num(TupleTableSlot *slot,
AttrNumber attrno,
bool *isNull)
{
return GetAttributeByNum(slot, attrno, isNull);
}
#endif
char *
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
{
@ -617,15 +593,6 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
return (char *) retval;
}
/* XXX name for catalogs */
#ifdef NOT_USED
char *
att_by_name(TupleTableSlot *slot, char *attname, bool *isNull)
{
return GetAttributeByName(slot, attname, isNull);
}
#endif
static void
ExecEvalFuncArgs(FunctionCachePtr fcache,
@ -732,9 +699,8 @@ ExecMakeFunctionResult(Node *node,
if (fcache->hasSetArg && argDone)
{
/* can only get here if input is an empty set. */
if (isDone)
*isDone = true;
*isNull = true;
*isDone = true;
return (Datum) 0;
}
}
@ -817,8 +783,8 @@ ExecMakeFunctionResult(Node *node,
else
{
result = (Datum) 0;
*isDone = true;
*isNull = true;
*isDone = true;
}
if (!*isDone)
@ -872,8 +838,8 @@ ExecMakeFunctionResult(Node *node,
else
{
/* A non-SQL function cannot return a set, at present. */
if (isDone)
*isDone = true;
*isDone = true;
/*
* If function is strict, and there are any NULL arguments,
* skip calling the function and return NULL.
@ -904,15 +870,7 @@ ExecMakeFunctionResult(Node *node,
* ExecEvalFunc
*
* Evaluate the functional result of a list of arguments by calling the
* function manager. Note that in the case of operator expressions, the
* optimizer had better have already replaced the operator OID with the
* appropriate function OID or we're hosed.
*
* old comments
* Presumably the function manager will not take null arguments, so we
* check for null arguments before sending the arguments to (fmgr).
*
* Returns the value of the functional expression.
* function manager.
* ----------------------------------------------------------------
*/
@ -929,8 +887,6 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
bool isDone;
/*
* an opclause is a list (op args). (I think)
*
* we extract the oid of the function associated with the op and then
* pass the work onto ExecMakeFunctionResult which evaluates the
* arguments and returns the result of calling the function on the
@ -954,7 +910,8 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull)
* call ExecMakeFunctionResult() with a dummy isDone that we ignore.
* We don't have operator whose arguments are sets.
*/
return ExecMakeFunctionResult((Node *) op, argList, econtext, isNull, &isDone);
return ExecMakeFunctionResult((Node *) op, argList, econtext,
isNull, &isDone);
}
/* ----------------------------------------------------------------
@ -973,8 +930,6 @@ ExecEvalFunc(Expr *funcClause,
FunctionCachePtr fcache;
/*
* an funcclause is a list (func args). (I think)
*
* we extract the oid of the function associated with the func node and
* then pass the work onto ExecMakeFunctionResult which evaluates the
* arguments and returns the result of calling the function on the
@ -996,7 +951,8 @@ ExecEvalFunc(Expr *funcClause,
fcache = func->func_fcache;
}
return ExecMakeFunctionResult((Node *) func, argList, econtext, isNull, isDone);
return ExecMakeFunctionResult((Node *) func, argList, econtext,
isNull, isDone);
}
/* ----------------------------------------------------------------
@ -1041,10 +997,7 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull)
* evaluation of 'not' is simple.. expr is false, then return 'true'
* and vice versa.
*/
if (DatumGetInt32(expr_value) == 0)
return (Datum) true;
return (Datum) false;
return BoolGetDatum(! DatumGetBool(expr_value));
}
/* ----------------------------------------------------------------
@ -1094,13 +1047,13 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull)
*/
if (*isNull)
AnyNull = true; /* remember we got a null */
else if (DatumGetInt32(clause_value) != 0)
else if (DatumGetBool(clause_value))
return clause_value;
}
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
return (Datum) false;
return BoolGetDatum(false);
}
/* ----------------------------------------------------------------
@ -1144,13 +1097,13 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull)
*/
if (*isNull)
AnyNull = true; /* remember we got a null */
else if (DatumGetInt32(clause_value) == 0)
else if (! DatumGetBool(clause_value))
return clause_value;
}
/* AnyNull is true if at least one clause evaluated to NULL */
*isNull = AnyNull;
return (Datum) (!AnyNull);
return BoolGetDatum(!AnyNull);
}
/* ----------------------------------------------------------------
@ -1195,7 +1148,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
* case statement is satisfied. A NULL result from the test is
* not considered true.
*/
if (DatumGetInt32(clause_value) != 0 && !*isNull)
if (DatumGetBool(clause_value) && !*isNull)
{
return ExecEvalExpr(wclause->result,
econtext,
@ -1221,19 +1174,15 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull)
*
* Recursively evaluate a targetlist or qualification expression.
*
* This routine is an inner loop routine and should be as fast
* The caller should already have switched into the temporary
* memory context econtext->ecxt_per_tuple_memory. The convenience
* entry point ExecEvalExprSwitchContext() is provided for callers
* who don't prefer to do the switch in an outer loop. We do not
* do the switch here because it'd be a waste of cycles during
* recursive entries to ExecEvalExpr().
*
* This routine is an inner loop routine and must be as fast
* as possible.
*
* Node comparison functions were replaced by macros for speed and to plug
* memory leaks incurred by using the planner's Lispy stuff for
* comparisons. Order of evaluation of node comparisons IS IMPORTANT;
* the macros do no checks. Order of evaluation:
*
* o an isnull check, largely to avoid coredumps since greg doubts this
* routine is called with a null ptr anyway in proper operation, but is
* not completely sure...
* o ExactNodeType checks.
* o clause checks or other checks where we look at the lfirst of something.
* ----------------------------------------------------------------
*/
Datum
@ -1244,25 +1193,21 @@ ExecEvalExpr(Node *expression,
{
Datum retDatum;
/* Set default values for result flags: non-null, not a set result */
*isNull = false;
*isDone = true;
/*
* Some callers don't care about is done and only want 1 result. They
* indicate this by passing NULL
*/
if (isDone)
*isDone = true;
/* Is this still necessary? Doubtful... */
if (expression == NULL)
{
*isNull = true;
return (Datum) 0;
}
/*
* here we dispatch the work to the appropriate type of function given
* the type of our expression.
*/
if (expression == NULL)
{
*isNull = true;
return (Datum) true;
}
switch (nodeTag(expression))
{
case T_Var:
@ -1350,8 +1295,27 @@ ExecEvalExpr(Node *expression,
} /* ExecEvalExpr() */
/*
* Same as above, but get into the right allocation context explicitly.
*/
Datum
ExecEvalExprSwitchContext(Node *expression,
ExprContext *econtext,
bool *isNull,
bool *isDone)
{
Datum retDatum;
MemoryContext oldContext;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
retDatum = ExecEvalExpr(expression, econtext, isNull, isDone);
MemoryContextSwitchTo(oldContext);
return retDatum;
}
/* ----------------------------------------------------------------
* ExecQual / ExecTargetList
* ExecQual / ExecTargetList / ExecProject
* ----------------------------------------------------------------
*/
@ -1386,6 +1350,8 @@ ExecEvalExpr(Node *expression,
bool
ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
{
bool result;
MemoryContext oldContext;
List *qlist;
/*
@ -1397,6 +1363,11 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
IncrProcessed();
/*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/*
* Evaluate the qual conditions one at a time. If we find a FALSE
* result, we can stop evaluating and return FALSE --- the AND result
@ -1409,6 +1380,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
* is NULL (one or more NULL subresult, with all the rest TRUE) and
* the caller has specified resultForNull = TRUE.
*/
result = true;
foreach(qlist, qual)
{
@ -1417,14 +1389,6 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
bool isNull;
bool isDone;
/*
* If there is a null clause, consider the qualification to fail.
* XXX is this still correct for constraints? It probably
* shouldn't happen at all ...
*/
if (clause == NULL)
return false;
/*
* pass isDone, but ignore it. We don't iterate over multiple
* returns in the qualifications.
@ -1434,16 +1398,24 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
if (isNull)
{
if (resultForNull == false)
return false; /* treat NULL as FALSE */
{
result = false; /* treat NULL as FALSE */
break;
}
}
else
{
if (DatumGetInt32(expr_value) == 0)
return false; /* definitely FALSE */
if (! DatumGetBool(expr_value))
{
result = false; /* definitely FALSE */
break;
}
}
}
return true;
MemoryContextSwitchTo(oldContext);
return result;
}
int
@ -1481,6 +1453,7 @@ ExecTargetList(List *targetlist,
ExprContext *econtext,
bool *isDone)
{
MemoryContext oldContext;
char nulls_array[64];
bool fjNullArray[64];
bool itemIsDoneArray[64];
@ -1506,6 +1479,11 @@ ExecTargetList(List *targetlist,
EV_nodeDisplay(targetlist);
EV_printf("\n");
/*
* Run in short-lived per-tuple context while computing expressions.
*/
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/*
* There used to be some klugy and demonstrably broken code here that
* special-cased the situation where targetlist == NIL. Now we just
@ -1563,10 +1541,10 @@ ExecTargetList(List *targetlist,
resdom = tle->resdom;
resind = resdom->resno - 1;
constvalue = (Datum) ExecEvalExpr(expr,
econtext,
&isNull,
&itemIsDone[resind]);
constvalue = ExecEvalExpr(expr,
econtext,
&isNull,
&itemIsDone[resind]);
values[resind] = constvalue;
@ -1597,7 +1575,10 @@ ExecTargetList(List *targetlist,
/* this is probably wrong: */
if (*isDone)
return (HeapTuple) NULL;
{
newTuple = NULL;
goto exit;
}
/*
* get the result from the inner node
@ -1681,10 +1662,10 @@ ExecTargetList(List *targetlist,
if (IsA(expr, Iter) &&itemIsDone[resind])
{
constvalue = (Datum) ExecEvalExpr(expr,
econtext,
&isNull,
&itemIsDone[resind]);
constvalue = ExecEvalExpr(expr,
econtext,
&isNull,
&itemIsDone[resind]);
if (itemIsDone[resind])
{
@ -1710,8 +1691,10 @@ ExecTargetList(List *targetlist,
}
/*
* form the new result tuple (in the "normal" context)
* form the new result tuple (in the caller's memory context!)
*/
MemoryContextSwitchTo(oldContext);
newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head);
exit:
@ -1726,6 +1709,9 @@ exit:
pfree(itemIsDone);
}
/* make sure we are in the right context if we did "goto exit" */
MemoryContextSwitchTo(oldContext);
return newTuple;
}