1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-03 20:02:46 +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

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: execAmi.c,v 1.48 2000/06/18 22:44:03 tgl Exp $
* $Id: execAmi.c,v 1.49 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -171,8 +171,8 @@ ExecBeginScan(Relation relation,
/* ----------------------------------------------------------------
* ExecCloseR
*
* closes the relation and scan descriptor for a scan or sort
* node. Also closes index relations and scans for index scans.
* closes the relation and scan descriptor for a scan node.
* Also closes index relations and scans for index scans.
* ----------------------------------------------------------------
*/
void
@ -197,20 +197,12 @@ ExecCloseR(Plan *node)
state = ((IndexScan *) node)->scan.scanstate;
break;
case T_Sort:
state = &(((Sort *) node)->sortstate->csstate);
break;
case T_Agg:
state = &(((Agg *) node)->aggstate->csstate);
break;
case T_TidScan:
state = ((TidScan *) node)->scan.scanstate;
break;
default:
elog(DEBUG, "ExecCloseR: not a scan or sort node!");
elog(DEBUG, "ExecCloseR: not a scan node!");
return;
}
@ -237,13 +229,12 @@ ExecCloseR(Plan *node)
if (IsA(node, IndexScan))
{
IndexScan *iscan = (IndexScan *) node;
IndexScanState *indexstate;
IndexScanState *indexstate = iscan->indxstate;
int numIndices;
RelationPtr indexRelationDescs;
IndexScanDescPtr indexScanDescs;
int i;
indexstate = iscan->indxstate;
numIndices = indexstate->iss_NumIndices;
indexRelationDescs = indexstate->iss_RelationDescs;
indexScanDescs = indexstate->iss_ScanDescs;

View File

@ -27,7 +27,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.121 2000/07/05 16:17:43 wieck Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execMain.c,v 1.122 2000/07/12 02:37:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -86,9 +86,12 @@ static void ExecCheckRTEPerms(RangeTblEntry *rte, CmdType operation,
* This routine must be called at the beginning of any execution of any
* query plan
*
* returns (AttrInfo*) which describes the attributes of the tuples to
* returns a TupleDesc which describes the attributes of the tuples to
* be returned by the query.
*
* 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.
* ----------------------------------------------------------------
*/
TupleDesc
@ -103,7 +106,8 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
{
estate->es_param_exec_vals = (ParamExecData *)
palloc(queryDesc->plantree->nParamExec * sizeof(ParamExecData));
memset(estate->es_param_exec_vals, 0, queryDesc->plantree->nParamExec * sizeof(ParamExecData));
MemSet(estate->es_param_exec_vals, 0,
queryDesc->plantree->nParamExec * sizeof(ParamExecData));
}
/*
@ -151,7 +155,6 @@ ExecutorStart(QueryDesc *queryDesc, EState *estate)
* EXEC_RETONE: return one tuple but don't 'retrieve' it
* used in postquel function processing
*
*
* ----------------------------------------------------------------
*/
TupleTableSlot *
@ -687,13 +690,6 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
*/
estate->es_range_table = rangeTable;
/*
* initialize the BaseId counter so node base_id's are assigned
* correctly. Someday baseid's will have to be stored someplace other
* than estate because they should be unique per query planned.
*/
estate->es_BaseId = 1;
/*
* initialize result relation stuff
*/
@ -793,7 +789,7 @@ InitPlan(CmdType operation, Query *parseTree, Plan *plan, EState *estate)
/*
* initialize the private state information for all the nodes in the
* query tree. This opens files, allocates storage and leaves us
* ready to start processing tuples..
* ready to start processing tuples.
*/
ExecInitNode(plan, estate, NULL);
@ -1589,7 +1585,7 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
{
int ndef = rel->rd_att->constr->num_defval;
AttrDefault *attrdef = rel->rd_att->constr->defval;
ExprContext *econtext = makeNode(ExprContext);
ExprContext *econtext = MakeExprContext(NULL, CurrentMemoryContext);
HeapTuple newtuple;
Node *expr;
bool isnull;
@ -1600,23 +1596,13 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
char *repl = NULL;
int i;
econtext->ecxt_scantuple = NULL; /* scan tuple slot */
econtext->ecxt_innertuple = NULL; /* inner tuple slot */
econtext->ecxt_outertuple = NULL; /* outer tuple slot */
econtext->ecxt_relation = NULL; /* relation */
econtext->ecxt_relid = 0; /* relid */
econtext->ecxt_param_list_info = NULL; /* param list info */
econtext->ecxt_param_exec_vals = NULL; /* exec param values */
econtext->ecxt_range_table = NULL; /* range table */
for (i = 0; i < ndef; i++)
{
if (!heap_attisnull(tuple, attrdef[i].adnum))
continue;
expr = (Node *) stringToNode(attrdef[i].adbin);
val = ExecEvalExpr(expr, econtext, &isnull, &isdone);
pfree(expr);
val = ExecEvalExprSwitchContext(expr, econtext, &isnull, &isdone);
if (isnull)
continue;
@ -1635,20 +1621,24 @@ ExecAttrDefault(Relation rel, HeapTuple tuple)
}
pfree(econtext);
if (repl == NULL)
return tuple;
{
/* no changes needed */
newtuple = tuple;
}
else
{
newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
newtuple = heap_modifytuple(tuple, rel, replValue, replNull, repl);
pfree(repl);
pfree(replNull);
pfree(replValue);
heap_freetuple(tuple);
}
pfree(repl);
heap_freetuple(tuple);
pfree(replNull);
pfree(replValue);
FreeMemoryContext(econtext);
return newtuple;
}
#endif
@ -1658,9 +1648,10 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
{
int ncheck = rel->rd_att->constr->num_check;
ConstrCheck *check = rel->rd_att->constr->check;
ExprContext *econtext = makeNode(ExprContext);
TupleTableSlot *slot = makeNode(TupleTableSlot);
RangeTblEntry *rte = makeNode(RangeTblEntry);
ExprContext *econtext = MakeExprContext(slot,
TransactionCommandContext);
List *rtlist;
List *qual;
int i;
@ -1677,17 +1668,21 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
rte->relid = RelationGetRelid(rel);
/* inh, inFromCl, inJoinSet, skipAcl won't be used, leave them zero */
rtlist = lcons(rte, NIL);
econtext->ecxt_scantuple = slot; /* scan tuple slot */
econtext->ecxt_innertuple = NULL; /* inner tuple slot */
econtext->ecxt_outertuple = NULL; /* outer tuple slot */
econtext->ecxt_relation = rel; /* relation */
econtext->ecxt_relid = 0; /* relid */
econtext->ecxt_param_list_info = NULL; /* param list info */
econtext->ecxt_param_exec_vals = NULL; /* exec param values */
econtext->ecxt_range_table = rtlist; /* range table */
econtext->ecxt_range_table = rtlist; /* phony range table */
/*
* Save the de-stringized constraint expressions in command-level
* memory context. XXX should build the above stuff there too,
* instead of doing it over for each tuple.
* XXX Is it sufficient to have just one es_result_relation_constraints
* in an inherited insert/update?
*/
if (estate->es_result_relation_constraints == NULL)
{
MemoryContext oldContext;
oldContext = MemoryContextSwitchTo(TransactionCommandContext);
estate->es_result_relation_constraints =
(List **) palloc(ncheck * sizeof(List *));
@ -1696,6 +1691,8 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
qual = (List *) stringToNode(check[i].ccbin);
estate->es_result_relation_constraints[i] = qual;
}
MemoryContextSwitchTo(oldContext);
}
for (i = 0; i < ncheck; i++)
@ -1714,16 +1711,15 @@ ExecRelCheck(Relation rel, HeapTuple tuple, EState *estate)
pfree(slot);
pfree(rte);
pfree(rtlist);
pfree(econtext);
FreeExprContext(econtext);
return (char *) NULL;
}
void
ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
{
Assert(rel->rd_att->constr);
if (rel->rd_att->constr->has_not_null)
@ -1732,9 +1728,10 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
for (attrChk = 1; attrChk <= rel->rd_att->natts; attrChk++)
{
if (rel->rd_att->attrs[attrChk - 1]->attnotnull && heap_attisnull(tuple, attrChk))
if (rel->rd_att->attrs[attrChk-1]->attnotnull &&
heap_attisnull(tuple, attrChk))
elog(ERROR, "%s: Fail to add null value in not null attribute %s",
caller, NameStr(rel->rd_att->attrs[attrChk - 1]->attname));
caller, NameStr(rel->rd_att->attrs[attrChk-1]->attname));
}
}
@ -1743,10 +1740,9 @@ ExecConstraints(char *caller, Relation rel, HeapTuple tuple, EState *estate)
char *failed;
if ((failed = ExecRelCheck(rel, tuple, estate)) != NULL)
elog(ERROR, "%s: rejected due to CHECK constraint %s", caller, failed);
elog(ERROR, "%s: rejected due to CHECK constraint %s",
caller, failed);
}
return;
}
TupleTableSlot *

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;
}

View File

@ -12,7 +12,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.11 2000/01/26 05:56:21 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.12 2000/07/12 02:37:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,7 +30,7 @@
* returns the next qualifying tuple in the direction specified
* in the global variable ExecDirection.
* The access method returns the next tuple and execScan() is
* responisble for checking the tuple returned against the qual-clause.
* responsible for checking the tuple returned against the qual-clause.
*
* Conditions:
* -- the "cursor" maintained by the AMI is positioned at the tuple
@ -39,59 +39,50 @@
* Initial States:
* -- the relation indicated is opened for scanning so that the
* "cursor" is positioned before the first qualifying tuple.
*
* May need to put startmmgr and endmmgr in here.
* ----------------------------------------------------------------
*/
TupleTableSlot *
ExecScan(Scan *node,
TupleTableSlot *(*accessMtd) ()) /* function returning a
* tuple */
ExecScanAccessMtd accessMtd) /* function returning a tuple */
{
CommonScanState *scanstate;
EState *estate;
List *qual;
bool isDone;
TupleTableSlot *slot;
TupleTableSlot *resultSlot;
HeapTuple newTuple;
ExprContext *econtext;
ProjectionInfo *projInfo;
/* ----------------
* initialize misc variables
* Fetch data from node
* ----------------
*/
newTuple = NULL;
slot = NULL;
estate = node->plan.state;
scanstate = node->scanstate;
/* ----------------
* get the expression context
* ----------------
*/
econtext = scanstate->cstate.cs_ExprContext;
qual = node->plan.qual;
/* ----------------
* initialize fields in ExprContext which don't change
* in the course of the scan..
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle.
* ----------------
*/
qual = node->plan.qual;
econtext->ecxt_relation = scanstate->css_currentRelation;
econtext->ecxt_relid = node->scanrelid;
ResetExprContext(econtext);
/* ----------------
* Check to see if we're still projecting out tuples from a previous
* scan tuple (because there is a function-returning-set in the
* projection expressions). If so, try to project another one.
* ----------------
*/
if (scanstate->cstate.cs_TupFromTlist)
{
projInfo = scanstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
if (!isDone)
return resultSlot;
/* Done with that source tuple... */
scanstate->cstate.cs_TupFromTlist = false;
}
/*
@ -100,27 +91,23 @@ ExecScan(Scan *node,
*/
for (;;)
{
slot = (TupleTableSlot *) (*accessMtd) (node);
TupleTableSlot *slot;
slot = (*accessMtd) (node);
/* ----------------
* if the slot returned by the accessMtd contains
* NULL, then it means there is nothing more to scan
* so we just return the empty slot...
*
* ... with invalid TupleDesc (not the same as in
* projInfo->pi_slot) and break upper MergeJoin node.
* New code below do what ExecProject() does. - vadim 02/26/98
* so we just return an empty slot, being careful to use
* the projection result slot so it has correct tupleDesc.
* ----------------
*/
if (TupIsNull(slot))
{
scanstate->cstate.cs_TupFromTlist = false;
resultSlot = scanstate->cstate.cs_ProjInfo->pi_slot;
return (TupleTableSlot *)
ExecStoreTuple(NULL,
resultSlot,
InvalidBuffer,
true);
return ExecStoreTuple(NULL,
scanstate->cstate.cs_ProjInfo->pi_slot,
InvalidBuffer,
true);
}
/* ----------------
@ -131,22 +118,28 @@ ExecScan(Scan *node,
/* ----------------
* check that the current tuple satisfies the qual-clause
* if our qualification succeeds then we
* if our qualification succeeds then we may
* leave the loop.
* ----------------
*/
/*
* add a check for non-nil qual here to avoid a function call to
*
* check for non-nil qual here to avoid a function call to
* ExecQual() when the qual is nil ... saves only a few cycles,
* but they add up ...
* ----------------
*/
if (!qual || ExecQual(qual, econtext, false))
break;
/* ----------------
* Tuple fails qual, so free per-tuple memory and try again.
* ----------------
*/
ResetExprContext(econtext);
}
/* ----------------
* form a projection tuple, store it in the result tuple
* Found a satisfactory scan tuple.
*
* Form a projection tuple, store it in the result tuple
* slot and return it.
* ----------------
*/

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.37 2000/04/12 17:15:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.38 2000/07/12 02:37:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -76,8 +76,7 @@
* by the access methods into the scan tuple slot.
*
* - ExecSeqScan() calls ExecStoreTuple() to take the result
* tuple from ExecTargetList() and place it into the result tuple
* slot.
* tuple from ExecProject() and place it into the result tuple slot.
*
* - ExecutePlan() calls ExecRetrieve() which gets the tuple out of
* the slot passed to it by calling ExecFetchTuple(). this tuple
@ -182,8 +181,8 @@ ExecCreateTupleTable(int initialSize) /* initial number of slots in
/* --------------------------------
* ExecDropTupleTable
*
* This pfrees the storage assigned to the tuple table and
* optionally pfrees the contents of the table also.
* This frees the storage used by the tuple table itself
* and optionally frees the contents of the table also.
* It is expected that this routine be called by EndPlan().
* --------------------------------
*/
@ -239,7 +238,6 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
*/
pfree(array);
pfree(table);
}
@ -252,13 +250,12 @@ ExecDropTupleTable(TupleTable table, /* tuple table */
*
* This routine is used to reserve slots in the table for
* use by the various plan nodes. It is expected to be
* called by the node init routines (ex: ExecInitNestLoop).
* called by the node init routines (ex: ExecInitNestLoop)
* once per slot needed by the node. Not all nodes need
* slots (some just pass tuples around).
* --------------------------------
*/
TupleTableSlot * /* return: the slot allocated in the tuple
* table */
TupleTableSlot *
ExecAllocTableSlot(TupleTable table)
{
int slotnum; /* new slot number */
@ -283,22 +280,12 @@ ExecAllocTableSlot(TupleTable table)
* pointers into _freed_ memory. This leads to bad ends. We
* now count the number of slots we will need and create all the
* slots we will need ahead of time. The if below should never
* happen now. Give a WARN if it does. -mer 4 Aug 1992
* happen now. Fail if it does. -mer 4 Aug 1992
* ----------------
*/
if (table->next >= table->size)
{
/*
* int newsize = NewTableSize(table->size);
*
* pfree(table->array); table->array = (Pointer) palloc(newsize *
* TableSlotSize); bzero(table->array, newsize * TableSlotSize);
* table->size = newsize;
*/
elog(NOTICE, "Plan requires more slots than are available");
elog(ERROR, "send mail to your local executor guru to fix this");
}
elog(ERROR, "Plan requires more slots than are available"
"\n\tsend mail to your local executor guru to fix this");
/* ----------------
* at this point, space in the table is guaranteed so we
@ -427,7 +414,7 @@ ExecClearTuple(TupleTableSlot *slot) /* slot in which to store tuple */
slot->val = (HeapTuple) NULL;
slot->ttc_shouldFree = true;/* probably useless code... */
slot->ttc_shouldFree = true; /* probably useless code... */
/* ----------------
* Drop the pin on the referenced buffer, if there is one.

View File

@ -1,22 +1,20 @@
/*-------------------------------------------------------------------------
*
* execUtils.c
* miscellanious executor utility routines
* miscellaneous executor utility routines
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.62 2000/07/05 23:11:14 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/execUtils.c,v 1.63 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecAssignNodeBaseInfo \
* ExecAssignDebugHooks > preforms misc work done in all the
* ExecAssignExprContext / init node routines.
* ExecAssignExprContext Common code for plan node init routines.
*
* ExecGetTypeInfo | old execCStructs interface
* ExecMakeTypeInfo | code from the version 1
@ -53,6 +51,7 @@
#include "miscadmin.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/memutils.h"
#include "utils/relcache.h"
#include "utils/syscache.h"
@ -137,59 +136,106 @@ DisplayTupleCount(FILE *statfp)
#endif
/* ----------------------------------------------------------------
* miscellanious init node support functions
* miscellaneous node-init support functions
*
* ExecAssignNodeBaseInfo - assigns the baseid field of the node
* ExecAssignDebugHooks - assigns the node's debugging hooks
* ExecAssignExprContext - assigns the node's expression context
* ----------------------------------------------------------------
*/
/* ----------------
* ExecAssignNodeBaseInfo
*
* as it says, this assigns the baseid field of the node and
* increments the counter in the estate. In addition, it initializes
* the base_parent field of the basenode.
* ----------------
*/
void
ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent)
{
int baseId;
baseId = estate->es_BaseId;
cstate->cs_base_id = baseId;
estate->es_BaseId = baseId + 1;
}
/* ----------------
* ExecAssignExprContext
*
* This initializes the ExprContext field. It is only necessary
* to do this for nodes which use ExecQual or ExecTargetList
* because those routines depend on econtext. Other nodes which
* dont have to evaluate expressions don't need to do this.
* 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.
*
* Note: we assume CurrentMemoryContext is the correct per-query context.
* This should be true during plan node initialization.
* ----------------
*/
void
ExecAssignExprContext(EState *estate, CommonState *commonstate)
{
ExprContext *econtext;
ExprContext *econtext = makeNode(ExprContext);
econtext = makeNode(ExprContext);
econtext->ecxt_scantuple = NULL; /* scan tuple slot */
econtext->ecxt_innertuple = NULL; /* inner tuple slot */
econtext->ecxt_outertuple = NULL; /* outer tuple slot */
econtext->ecxt_relation = NULL; /* relation */
econtext->ecxt_relid = 0; /* relid */
econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_scantuple = NULL;
econtext->ecxt_innertuple = NULL;
econtext->ecxt_outertuple = NULL;
econtext->ecxt_per_query_memory = CurrentMemoryContext;
/*
* Create working memory for expression evaluation in this context.
*/
econtext->ecxt_per_tuple_memory =
AllocSetContextCreate(CurrentMemoryContext,
"PlanExprContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
econtext->ecxt_param_exec_vals = estate->es_param_exec_vals;
econtext->ecxt_range_table = estate->es_range_table; /* range table */
econtext->ecxt_param_list_info = estate->es_param_list_info;
econtext->ecxt_aggvalues = NULL;
econtext->ecxt_aggnulls = NULL;
econtext->ecxt_range_table = estate->es_range_table;
commonstate->cs_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.
*/
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_range_table = NIL;
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!
*/
void
FreeExprContext(ExprContext *econtext)
{
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
}
/* ----------------------------------------------------------------
* Result slot tuple type and ProjectionInfo support
* ----------------------------------------------------------------
@ -390,6 +436,7 @@ ExecFreeExprContext(CommonState *commonstate)
* clean up memory used.
* ----------------
*/
MemoryContextDelete(econtext->ecxt_per_tuple_memory);
pfree(econtext);
commonstate->cs_ExprContext = NULL;
}
@ -398,6 +445,7 @@ ExecFreeExprContext(CommonState *commonstate)
* ExecFreeTypeInfo
* ----------------
*/
#ifdef NOT_USED
void
ExecFreeTypeInfo(CommonState *commonstate)
{
@ -414,6 +462,7 @@ ExecFreeTypeInfo(CommonState *commonstate)
FreeTupleDesc(tupDesc);
commonstate->cs_ResultTupleSlot->ttc_tupleDescriptor = NULL;
}
#endif
/* ----------------------------------------------------------------
* the following scan type support functions are for
@ -974,8 +1023,8 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
if (predicate != NULL)
{
if (econtext == NULL)
econtext = makeNode(ExprContext);
econtext->ecxt_scantuple = slot;
econtext = MakeExprContext(slot,
TransactionCommandContext);
/* Skip this index-update if the predicate isn't satisfied */
if (!ExecQual((List *) predicate, econtext, false))
@ -1023,7 +1072,7 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
pfree(result);
}
if (econtext != NULL)
pfree(econtext);
FreeExprContext(econtext);
}
void

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.35 2000/06/28 03:31:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -57,20 +57,18 @@ ProjectAttribute(TupleDesc TD,
HeapTuple tup,
bool *isnullP)
{
Datum val,
valueP;
Datum val;
Var *attrVar = (Var *) tlist->expr;
AttrNumber attrno = attrVar->varattno;
val = heap_getattr(tup, attrno, TD, isnullP);
if (*isnullP)
return (Datum) NULL;
valueP = datumCopy(val,
TD->attrs[attrno - 1]->atttypid,
TD->attrs[attrno - 1]->attbyval,
(Size) TD->attrs[attrno - 1]->attlen);
return valueP;
if (*isnullP)
return (Datum) 0;
return datumCopy(val,
TD->attrs[attrno - 1]->attbyval,
TD->attrs[attrno - 1]->attlen);
}
static execution_state *
@ -351,10 +349,18 @@ postquel_function(FunctionCallInfo fcinfo,
List *func_tlist,
bool *isDone)
{
MemoryContext oldcontext;
execution_state *es;
Datum result = 0;
CommandId savedId;
/*
* Switch to context in which the fcache lives. This ensures that
* parsetrees, plans, etc, will have sufficient lifetime. The
* sub-executor is responsible for deleting per-tuple information.
*/
oldcontext = MemoryContextSwitchTo(fcache->fcacheCxt);
/*
* Before we start do anything we must save CurrentScanCommandId to
* restore it before return to upper Executor. Also, we have to set
@ -416,6 +422,7 @@ postquel_function(FunctionCallInfo fcinfo,
* Let caller know we're finished.
*/
*isDone = true;
MemoryContextSwitchTo(oldcontext);
return (fcache->oneResult) ? result : (Datum) NULL;
}
@ -426,5 +433,8 @@ postquel_function(FunctionCallInfo fcinfo,
Assert(LAST_POSTQUEL_COMMAND(es));
*isDone = false;
MemoryContextSwitchTo(oldcontext);
return result;
}

View File

@ -32,7 +32,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.68 2000/06/28 03:31:33 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.69 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -50,6 +50,7 @@
#include "parser/parse_type.h"
#include "utils/syscache.h"
#include "utils/tuplesort.h"
#include "utils/datum.h"
/*
* AggStatePerAggData - per-aggregate working state for the Agg scan
@ -101,13 +102,15 @@ typedef struct AggStatePerAggData
initValue2IsNull;
/*
* We need the len and byval info for the agg's input and transition
* data types in order to know how to copy/delete values.
* We need the len and byval info for the agg's input, result, and
* transition data types in order to know how to copy/delete values.
*/
int inputtypeLen,
resulttypeLen,
transtype1Len,
transtype2Len;
bool inputtypeByVal,
resulttypeByVal,
transtype1ByVal,
transtype2ByVal;
@ -143,13 +146,16 @@ typedef struct AggStatePerAggData
static void initialize_aggregate(AggStatePerAgg peraggstate);
static void advance_transition_functions(AggStatePerAgg peraggstate,
Datum newVal, bool isNull);
static void process_sorted_aggregate(AggState *aggstate,
AggStatePerAgg peraggstate);
static void finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull);
static Datum copyDatum(Datum val, int typLen, bool typByVal);
/*
* Initialize one aggregate for a new set of input values.
*
* When called, CurrentMemoryContext should be the per-query context.
*/
static void
initialize_aggregate(AggStatePerAgg peraggstate)
@ -177,23 +183,14 @@ initialize_aggregate(AggStatePerAgg peraggstate)
/*
* (Re)set value1 and value2 to their initial values.
*
* Note that when the initial values are pass-by-ref, we just reuse
* them without copying for each group. Hence, transition function
* had better not scribble on its input!
*/
if (OidIsValid(peraggstate->xfn1_oid) &&
!peraggstate->initValue1IsNull)
peraggstate->value1 = copyDatum(peraggstate->initValue1,
peraggstate->transtype1Len,
peraggstate->transtype1ByVal);
else
peraggstate->value1 = (Datum) NULL;
peraggstate->value1 = peraggstate->initValue1;
peraggstate->value1IsNull = peraggstate->initValue1IsNull;
if (OidIsValid(peraggstate->xfn2_oid) &&
!peraggstate->initValue2IsNull)
peraggstate->value2 = copyDatum(peraggstate->initValue2,
peraggstate->transtype2Len,
peraggstate->transtype2ByVal);
else
peraggstate->value2 = (Datum) NULL;
peraggstate->value2 = peraggstate->initValue2;
peraggstate->value2IsNull = peraggstate->initValue2IsNull;
/* ------------------------------------------
@ -211,6 +208,9 @@ initialize_aggregate(AggStatePerAgg peraggstate)
/*
* Given a new input value, advance the transition functions of an aggregate.
*
* When called, CurrentMemoryContext should be the context we want transition
* function results to be delivered into on this cycle.
*
* Note: if the agg does not have usenulls set, null inputs will be filtered
* out before reaching here.
*/
@ -237,12 +237,13 @@ advance_transition_functions(AggStatePerAgg peraggstate,
* XXX We assume, without having checked, that the agg's input
* type is binary-compatible with its transtype1!
*
* We have to copy the datum since the tuple from which it came
* We had better copy the datum if it is pass-by-ref, since
* the given pointer may be pointing into a scan tuple that
* will be freed on the next iteration of the scan.
*/
peraggstate->value1 = copyDatum(newVal,
peraggstate->transtype1Len,
peraggstate->transtype1ByVal);
peraggstate->value1 = datumCopy(newVal,
peraggstate->transtype1ByVal,
peraggstate->transtype1Len);
peraggstate->value1IsNull = false;
peraggstate->noInitValue = false;
}
@ -264,8 +265,18 @@ advance_transition_functions(AggStatePerAgg peraggstate,
}
else
newVal = FunctionCallInvoke(&fcinfo);
if (!peraggstate->transtype1ByVal && !peraggstate->value1IsNull)
pfree(DatumGetPointer(peraggstate->value1));
/*
* If the transition function was uncooperative, it may have
* given us a pass-by-ref result that points at the scan tuple
* or the prior-cycle working memory. Copy it into the active
* context if it doesn't look right.
*/
if (!peraggstate->transtype1ByVal && !fcinfo.isnull &&
! MemoryContextContains(CurrentMemoryContext,
DatumGetPointer(newVal)))
newVal = datumCopy(newVal,
peraggstate->transtype1ByVal,
peraggstate->transtype1Len);
peraggstate->value1 = newVal;
peraggstate->value1IsNull = fcinfo.isnull;
}
@ -287,70 +298,116 @@ advance_transition_functions(AggStatePerAgg peraggstate,
}
else
newVal = FunctionCallInvoke(&fcinfo);
if (!peraggstate->transtype2ByVal && !peraggstate->value2IsNull)
pfree(DatumGetPointer(peraggstate->value2));
/*
* If the transition function was uncooperative, it may have
* given us a pass-by-ref result that points at the scan tuple
* or the prior-cycle working memory. Copy it into the active
* context if it doesn't look right.
*/
if (!peraggstate->transtype2ByVal && !fcinfo.isnull &&
! MemoryContextContains(CurrentMemoryContext,
DatumGetPointer(newVal)))
newVal = datumCopy(newVal,
peraggstate->transtype2ByVal,
peraggstate->transtype2Len);
peraggstate->value2 = newVal;
peraggstate->value2IsNull = fcinfo.isnull;
}
}
/*
* Compute the final value of one aggregate.
* Run the transition functions for a DISTINCT aggregate. This is called
* after we have completed entering all the input values into the sort
* object. We complete the sort, read out the value in sorted order, and
* run the transition functions on each non-duplicate value.
*
* When called, CurrentMemoryContext should be the per-query context.
*/
static void
finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull)
process_sorted_aggregate(AggState *aggstate,
AggStatePerAgg peraggstate)
{
Aggref *aggref = peraggstate->aggref;
FunctionCallInfoData fcinfo;
Datum oldVal = (Datum) 0;
bool haveOldVal = false;
MemoryContext oldContext;
Datum newVal;
bool isNull;
MemSet(&fcinfo, 0, sizeof(fcinfo));
tuplesort_performsort(peraggstate->sortstate);
/*
* If it's a DISTINCT aggregate, all we've done so far is to stuff the
* input values into the sort object. Complete the sort, then run the
* transition functions on the non-duplicate values. Note that
* DISTINCT always suppresses nulls, per SQL spec, regardless of
* usenulls.
* Note: if input type is pass-by-ref, the datums returned by the sort
* are freshly palloc'd in the per-query context, so we must be careful
* to pfree them when they are no longer needed.
*/
if (aggref->aggdistinct)
{
Datum oldVal = (Datum) 0;
bool haveOldVal = false;
Datum newVal;
bool isNull;
tuplesort_performsort(peraggstate->sortstate);
while (tuplesort_getdatum(peraggstate->sortstate, true,
&newVal, &isNull))
while (tuplesort_getdatum(peraggstate->sortstate, true,
&newVal, &isNull))
{
/*
* DISTINCT always suppresses nulls, per SQL spec, regardless of
* the aggregate's usenulls setting.
*/
if (isNull)
continue;
/*
* Clear and select the current working context for evaluation of
* the equality function and transition functions.
*/
MemoryContextReset(aggstate->agg_cxt[aggstate->which_cxt]);
oldContext =
MemoryContextSwitchTo(aggstate->agg_cxt[aggstate->which_cxt]);
if (haveOldVal &&
DatumGetBool(FunctionCall2(&peraggstate->equalfn,
oldVal, newVal)))
{
/* equal to prior, so forget this one */
if (!peraggstate->inputtypeByVal)
pfree(DatumGetPointer(newVal));
/* note we do NOT flip contexts in this case... */
}
else
{
if (isNull)
continue;
if (haveOldVal)
{
if (DatumGetBool(FunctionCall2(&peraggstate->equalfn,
oldVal, newVal)))
{
/* equal to prior, so forget this one */
if (!peraggstate->inputtypeByVal)
pfree(DatumGetPointer(newVal));
continue;
}
}
advance_transition_functions(peraggstate, newVal, false);
/*
* Make the other context current so that this transition
* result is preserved.
*/
aggstate->which_cxt = 1 - aggstate->which_cxt;
/* forget the old value, if any */
if (haveOldVal && !peraggstate->inputtypeByVal)
pfree(DatumGetPointer(oldVal));
oldVal = newVal;
haveOldVal = true;
}
if (haveOldVal && !peraggstate->inputtypeByVal)
pfree(DatumGetPointer(oldVal));
tuplesort_end(peraggstate->sortstate);
peraggstate->sortstate = NULL;
MemoryContextSwitchTo(oldContext);
}
if (haveOldVal && !peraggstate->inputtypeByVal)
pfree(DatumGetPointer(oldVal));
tuplesort_end(peraggstate->sortstate);
peraggstate->sortstate = NULL;
}
/*
* Compute the final value of one aggregate.
*
* When called, CurrentMemoryContext should be the context where we want
* final values delivered (ie, the per-output-tuple expression context).
*/
static void
finalize_aggregate(AggStatePerAgg peraggstate,
Datum *resultVal, bool *resultIsNull)
{
FunctionCallInfoData fcinfo;
MemSet(&fcinfo, 0, sizeof(fcinfo));
/*
* Now apply the agg's finalfn, or substitute the appropriate
* Apply the agg's finalfn, or substitute the appropriate
* transition value if there is no finalfn.
*
* XXX For now, only apply finalfn if we got at least one non-null input
@ -403,35 +460,27 @@ finalize_aggregate(AggStatePerAgg peraggstate,
/* Return value1 */
*resultVal = peraggstate->value1;
*resultIsNull = peraggstate->value1IsNull;
/* prevent pfree below */
peraggstate->value1IsNull = true;
}
else if (OidIsValid(peraggstate->xfn2_oid))
{
/* Return value2 */
*resultVal = peraggstate->value2;
*resultIsNull = peraggstate->value2IsNull;
/* prevent pfree below */
peraggstate->value2IsNull = true;
}
else
elog(ERROR, "ExecAgg: no valid transition functions??");
/*
* Release any per-group working storage, unless we're passing it back
* as the result of the aggregate.
* If result is pass-by-ref, make sure it is in the right context.
*/
if (OidIsValid(peraggstate->xfn1_oid) &&
!peraggstate->value1IsNull &&
!peraggstate->transtype1ByVal)
pfree(DatumGetPointer(peraggstate->value1));
if (OidIsValid(peraggstate->xfn2_oid) &&
!peraggstate->value2IsNull &&
!peraggstate->transtype2ByVal)
pfree(DatumGetPointer(peraggstate->value2));
if (!peraggstate->resulttypeByVal && ! *resultIsNull &&
! MemoryContextContains(CurrentMemoryContext,
DatumGetPointer(*resultVal)))
*resultVal = datumCopy(*resultVal,
peraggstate->resulttypeByVal,
peraggstate->resulttypeLen);
}
/* ---------------------------------------
*
* ExecAgg -
@ -461,6 +510,7 @@ ExecAgg(Agg *node)
Datum *aggvalues;
bool *aggnulls;
AggStatePerAgg peragg;
MemoryContext oldContext;
TupleTableSlot *resultSlot;
HeapTuple inputTuple;
int aggno;
@ -481,14 +531,18 @@ ExecAgg(Agg *node)
peragg = aggstate->peragg;
/*
* We loop retrieving groups until we find one matching
* node->plan.qual
* We loop retrieving groups until we find one matching node->plan.qual
*/
do
{
if (aggstate->agg_done)
return NULL;
/*
* Clear the per-output-tuple context for each group
*/
MemoryContextReset(aggstate->tup_cxt);
/*
* Initialize working state for a new input tuple group
*/
@ -514,6 +568,17 @@ ExecAgg(Agg *node)
break;
econtext->ecxt_scantuple = outerslot;
/*
* Clear and select the current working context for evaluation
* of the input expressions and transition functions at this
* input tuple.
*/
econtext->ecxt_per_tuple_memory =
aggstate->agg_cxt[aggstate->which_cxt];
ResetExprContext(econtext);
oldContext =
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
{
AggStatePerAgg peraggstate = &peragg[aggno];
@ -527,13 +592,26 @@ ExecAgg(Agg *node)
continue; /* ignore this tuple for this agg */
if (aggref->aggdistinct)
{
/* putdatum has to be called in per-query context */
MemoryContextSwitchTo(oldContext);
tuplesort_putdatum(peraggstate->sortstate,
newVal, isNull);
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
}
else
advance_transition_functions(peraggstate,
newVal, isNull);
}
/*
* Make the other context current so that these transition
* results are preserved.
*/
aggstate->which_cxt = 1 - aggstate->which_cxt;
MemoryContextSwitchTo(oldContext);
/*
* Keep a copy of the first input tuple for the projection.
* (We only need one since only the GROUP BY columns in it can
@ -546,14 +624,38 @@ ExecAgg(Agg *node)
/*
* Done scanning input tuple group. Finalize each aggregate
* calculation.
* calculation, and stash results in the per-output-tuple context.
*
* This is a bit tricky when there are both DISTINCT and plain
* aggregates: we must first finalize all the plain aggs and then all
* the DISTINCT ones. This is needed because the last transition
* values for the plain aggs are stored in the not-current working
* context, and we have to evaluate those aggs (and stash the results
* in the output tup_cxt!) before we start flipping contexts again
* in process_sorted_aggregate.
*/
oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
{
AggStatePerAgg peraggstate = &peragg[aggno];
finalize_aggregate(peraggstate,
&aggvalues[aggno], &aggnulls[aggno]);
if (! peraggstate->aggref->aggdistinct)
finalize_aggregate(peraggstate,
&aggvalues[aggno], &aggnulls[aggno]);
}
MemoryContextSwitchTo(oldContext);
for (aggno = 0; aggno < aggstate->numaggs; aggno++)
{
AggStatePerAgg peraggstate = &peragg[aggno];
if (peraggstate->aggref->aggdistinct)
{
process_sorted_aggregate(aggstate, peraggstate);
oldContext = MemoryContextSwitchTo(aggstate->tup_cxt);
finalize_aggregate(peraggstate,
&aggvalues[aggno], &aggnulls[aggno]);
MemoryContextSwitchTo(oldContext);
}
}
/*
@ -584,7 +686,7 @@ ExecAgg(Agg *node)
/*
* If inputtuple==NULL (ie, the outerPlan didn't return
* anything), create a dummy all-nulls input tuple for use by
* execProject. 99.44% of the time this is a waste of cycles,
* ExecProject. 99.44% of the time this is a waste of cycles,
* because ordinarily the projected output tuple's targetlist
* cannot contain any direct (non-aggregated) references to
* input columns, so the dummy tuple will not be referenced.
@ -619,7 +721,8 @@ ExecAgg(Agg *node)
/*
* Store the representative input tuple in the tuple table slot
* reserved for it.
* reserved for it. The tuple will be deleted when it is cleared
* from the slot.
*/
ExecStoreTuple(inputTuple,
aggstate->csstate.css_ScanTupleSlot,
@ -627,6 +730,11 @@ ExecAgg(Agg *node)
true);
econtext->ecxt_scantuple = aggstate->csstate.css_ScanTupleSlot;
/*
* Do projection and qual check in the per-output-tuple context.
*/
econtext->ecxt_per_tuple_memory = aggstate->tup_cxt;
/*
* Form a projection tuple using the aggregate results and the
* representative input tuple. Store it in the result tuple slot.
@ -701,11 +809,33 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
}
/*
* assign node's base id and create expression context
* Create expression context
*/
ExecAssignNodeBaseInfo(estate, &aggstate->csstate.cstate, (Plan *) parent);
ExecAssignExprContext(estate, &aggstate->csstate.cstate);
/*
* We actually need three separate expression memory contexts: one
* for calculating per-output-tuple values (ie, the finished aggregate
* results), and two that we ping-pong between for per-input-tuple
* evaluation of input expressions and transition functions. The
* context made by ExecAssignExprContext() is used as the output context.
*/
aggstate->tup_cxt =
aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory;
aggstate->agg_cxt[0] =
AllocSetContextCreate(CurrentMemoryContext,
"AggExprContext1",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
aggstate->agg_cxt[1] =
AllocSetContextCreate(CurrentMemoryContext,
"AggExprContext2",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
aggstate->which_cxt = 0;
#define AGG_NSLOTS 2
/*
@ -769,16 +899,20 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
/* Fill in the peraggstate data */
peraggstate->aggref = aggref;
aggTuple = SearchSysCacheTuple(AGGNAME,
PointerGetDatum(aggname),
ObjectIdGetDatum(aggref->basetype),
0, 0);
aggTuple = SearchSysCacheTupleCopy(AGGNAME,
PointerGetDatum(aggname),
ObjectIdGetDatum(aggref->basetype),
0, 0);
if (!HeapTupleIsValid(aggTuple))
elog(ERROR, "ExecAgg: cache lookup failed for aggregate %s(%s)",
aggname,
typeidTypeName(aggref->basetype));
aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple);
typeInfo = typeidType(aggform->aggfinaltype);
peraggstate->resulttypeLen = typeLen(typeInfo);
peraggstate->resulttypeByVal = typeByVal(typeInfo);
peraggstate->initValue1 =
AggNameGetInitVal(aggname,
aggform->aggbasetype,
@ -846,6 +980,8 @@ ExecInitAgg(Agg *node, EState *estate, Plan *parent)
peraggstate->sortOperator = any_ordering_op(inputType);
peraggstate->sortstate = NULL;
}
heap_freetuple(aggTuple);
}
return TRUE;
@ -866,6 +1002,17 @@ ExecEndAgg(Agg *node)
Plan *outerPlan;
ExecFreeProjectionInfo(&aggstate->csstate.cstate);
/*
* Make sure ExecFreeExprContext() frees the right expr context...
*/
aggstate->csstate.cstate.cs_ExprContext->ecxt_per_tuple_memory =
aggstate->tup_cxt;
ExecFreeExprContext(&aggstate->csstate.cstate);
/*
* ... and I free the others.
*/
MemoryContextDelete(aggstate->agg_cxt[0]);
MemoryContextDelete(aggstate->agg_cxt[1]);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
@ -890,28 +1037,4 @@ ExecReScanAgg(Agg *node, ExprContext *exprCtxt, Plan *parent)
*/
if (((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
}
/*
* Helper routine to make a copy of a Datum.
*
* NB: input had better not be a NULL; might cause null-pointer dereference.
*/
static Datum
copyDatum(Datum val, int typLen, bool typByVal)
{
if (typByVal)
return val;
else
{
char *newVal;
if (typLen == -1) /* variable length type? */
typLen = VARSIZE((struct varlena *) DatumGetPointer(val));
newVal = (char *) palloc(typLen);
memcpy(newVal, DatumGetPointer(val), typLen);
return PointerGetDatum(newVal);
}
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.34 2000/06/17 21:48:49 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeAppend.c,v 1.35 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -219,16 +219,12 @@ ExecInitAppend(Append *node, EState *estate, Plan *parent)
node->appendstate = appendstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks
* Miscellaneous initialization
*
* Append plans don't have expression contexts because they
* never call ExecQual or ExecTargetList.
* never call ExecQual or ExecProject.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &appendstate->cstate, parent);
#define APPEND_NSLOTS 1
/* ----------------
@ -380,7 +376,7 @@ ExecCountSlotsAppend(Append *node)
*
* Handles the iteration over the multiple scans.
*
* NOTE: Can't call this ExecAppend, that name is used in execMain.l
* NOTE: Can't call this ExecAppend, that name is used in execMain.
* ----------------------------------------------------------------
*/
TupleTableSlot *

View File

@ -15,7 +15,7 @@
* locate group boundaries.
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.36 2000/05/30 04:24:45 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.37 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -68,13 +68,11 @@ ExecGroupEveryTuple(Group *node)
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
TupleTableSlot *outerslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone;
/* ---------------------
@ -84,14 +82,16 @@ ExecGroupEveryTuple(Group *node)
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
estate = node->plan.state;
econtext = grpstate->csstate.cstate.cs_ExprContext;
tupdesc = ExecGetScanType(&grpstate->csstate);
/* if we haven't returned first tuple of new group yet ... */
/*
* We need not call ResetExprContext here because execTuplesMatch
* will reset the per-tuple memory context once per input tuple.
*/
/* if we haven't returned first tuple of a new group yet ... */
if (grpstate->grp_useFirstTuple)
{
grpstate->grp_useFirstTuple = FALSE;
@ -130,7 +130,8 @@ ExecGroupEveryTuple(Group *node)
if (!execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
grpstate->eqfunctions))
grpstate->eqfunctions,
econtext->ecxt_per_tuple_memory))
{
/*
@ -179,13 +180,11 @@ ExecGroupOneTuple(Group *node)
EState *estate;
ExprContext *econtext;
TupleDesc tupdesc;
HeapTuple outerTuple = NULL;
HeapTuple firsttuple;
TupleTableSlot *outerslot;
ProjectionInfo *projInfo;
TupleTableSlot *resultSlot;
bool isDone;
/* ---------------------
@ -195,13 +194,15 @@ ExecGroupOneTuple(Group *node)
grpstate = node->grpstate;
if (grpstate->grp_done)
return NULL;
estate = node->plan.state;
econtext = node->grpstate->csstate.cstate.cs_ExprContext;
tupdesc = ExecGetScanType(&grpstate->csstate);
/*
* We need not call ResetExprContext here because execTuplesMatch
* will reset the per-tuple memory context once per input tuple.
*/
firsttuple = grpstate->grp_firstTuple;
if (firsttuple == NULL)
{
@ -237,7 +238,8 @@ ExecGroupOneTuple(Group *node)
if (!execTuplesMatch(firsttuple, outerTuple,
tupdesc,
node->numCols, node->grpColIdx,
grpstate->eqfunctions))
grpstate->eqfunctions,
econtext->ecxt_per_tuple_memory))
break;
}
@ -296,10 +298,8 @@ ExecInitGroup(Group *node, EState *estate, Plan *parent)
grpstate->grp_firstTuple = NULL;
/*
* assign node's base id and create expression context
* create expression context
*/
ExecAssignNodeBaseInfo(estate, &grpstate->csstate.cstate,
(Plan *) parent);
ExecAssignExprContext(estate, &grpstate->csstate.cstate);
#define GROUP_NSLOTS 2
@ -360,6 +360,7 @@ ExecEndGroup(Group *node)
grpstate = node->grpstate;
ExecFreeProjectionInfo(&grpstate->csstate.cstate);
ExecFreeExprContext(&grpstate->csstate.cstate);
outerPlan = outerPlan(node);
ExecEndNode(outerPlan, (Plan *) node);
@ -406,6 +407,9 @@ ExecReScanGroup(Group *node, ExprContext *exprCtxt, Plan *parent)
* numCols: the number of attributes to be examined
* matchColIdx: array of attribute column numbers
* eqFunctions: array of fmgr lookup info for the equality functions to use
* evalContext: short-term memory context for executing the functions
*
* NB: evalContext is reset each time!
*/
bool
execTuplesMatch(HeapTuple tuple1,
@ -413,16 +417,25 @@ execTuplesMatch(HeapTuple tuple1,
TupleDesc tupdesc,
int numCols,
AttrNumber *matchColIdx,
FmgrInfo *eqfunctions)
FmgrInfo *eqfunctions,
MemoryContext evalContext)
{
MemoryContext oldContext;
bool result;
int i;
/* Reset and switch into the temp context. */
MemoryContextReset(evalContext);
oldContext = MemoryContextSwitchTo(evalContext);
/*
* We cannot report a match without checking all the fields, but we
* can report a non-match as soon as we find unequal fields. So,
* start comparing at the last field (least significant sort key).
* That's the most likely to be different...
*/
result = true;
for (i = numCols; --i >= 0;)
{
AttrNumber att = matchColIdx[i];
@ -442,7 +455,10 @@ execTuplesMatch(HeapTuple tuple1,
&isNull2);
if (isNull1 != isNull2)
return FALSE; /* one null and one not; they aren't equal */
{
result = false; /* one null and one not; they aren't equal */
break;
}
if (isNull1)
continue; /* both are null, treat as equal */
@ -451,10 +467,15 @@ execTuplesMatch(HeapTuple tuple1,
if (! DatumGetBool(FunctionCall2(&eqfunctions[i],
attr1, attr2)))
return FALSE;
{
result = false; /* they aren't equal */
break;
}
}
return TRUE;
MemoryContextSwitchTo(oldContext);
return result;
}
/*

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* $Id: nodeHash.c,v 1.48 2000/06/28 03:31:34 tgl Exp $
* $Id: nodeHash.c,v 1.49 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,7 +28,8 @@
#include "executor/nodeHash.h"
#include "executor/nodeHashjoin.h"
#include "miscadmin.h"
#include "parser/parse_expr.h"
#include "parser/parse_type.h"
static int hashFunc(Datum key, int len, bool byVal);
@ -45,7 +46,7 @@ ExecHash(Hash *node)
EState *estate;
HashState *hashstate;
Plan *outerNode;
Var *hashkey;
Node *hashkey;
HashJoinTable hashtable;
TupleTableSlot *slot;
ExprContext *econtext;
@ -139,12 +140,9 @@ ExecInitHash(Hash *node, EState *estate, Plan *parent)
/* ----------------
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &hashstate->cstate, parent);
ExecAssignExprContext(estate, &hashstate->cstate);
/* ----------------
@ -204,6 +202,7 @@ ExecEndHash(Hash *node)
* ----------------
*/
ExecFreeProjectionInfo(&hashstate->cstate);
ExecFreeExprContext(&hashstate->cstate);
/* ----------------
* shut down the subplan
@ -236,6 +235,7 @@ ExecHashTableCreate(Hash *node)
int totalbuckets;
int bucketsize;
int i;
Type typeInfo;
MemoryContext oldcxt;
/* ----------------
@ -346,6 +346,14 @@ ExecHashTableCreate(Hash *node)
hashtable->innerBatchSize = NULL;
hashtable->outerBatchSize = NULL;
/* ----------------
* Get info about the datatype of the hash key.
* ----------------
*/
typeInfo = typeidType(exprType(node->hashkey));
hashtable->typByVal = typeByVal(typeInfo);
hashtable->typLen = typeLen(typeInfo);
/* ----------------
* Create temporary memory contexts in which to keep the hashtable
* working storage. See notes in executor/hashjoin.h.
@ -448,7 +456,7 @@ ExecHashTableDestroy(HashJoinTable hashtable)
void
ExecHashTableInsert(HashJoinTable hashtable,
ExprContext *econtext,
Var *hashkey)
Node *hashkey)
{
int bucketno = ExecHashGetBucket(hashtable, econtext, hashkey);
TupleTableSlot *slot = econtext->ecxt_innertuple;
@ -508,43 +516,44 @@ ExecHashTableInsert(HashJoinTable hashtable,
int
ExecHashGetBucket(HashJoinTable hashtable,
ExprContext *econtext,
Var *hashkey)
Node *hashkey)
{
int bucketno;
Datum keyval;
bool isNull;
bool isDone;
/* ----------------
* Get the join attribute value of the tuple
*
* ...It's quick hack - use ExecEvalExpr instead of ExecEvalVar:
* hashkey may be T_ArrayRef, not just T_Var. - vadim 04/22/97
* We reset the eval context each time to avoid any possibility
* of memory leaks in the hash function.
* ----------------
*/
keyval = ExecEvalExpr((Node *) hashkey, econtext, &isNull, NULL);
ResetExprContext(econtext);
/*
* keyval could be null, so we better point it to something valid
* before trying to run hashFunc on it. --djm 8/17/96
*/
if (isNull)
{
execConstByVal = 0;
execConstLen = 0;
keyval = (Datum) "";
}
keyval = ExecEvalExprSwitchContext(hashkey, econtext,
&isNull, &isDone);
/* ------------------
* compute the hash function
* ------------------
*/
bucketno = hashFunc(keyval, execConstLen, execConstByVal) % hashtable->totalbuckets;
if (isNull)
{
bucketno = 0;
}
else
{
bucketno = hashFunc(keyval, hashtable->typLen, hashtable->typByVal)
% hashtable->totalbuckets;
}
#ifdef HJDEBUG
if (bucketno >= hashtable->nbuckets)
printf("hash(%d) = %d SAVED\n", keyval, bucketno);
printf("hash(%ld) = %d SAVED\n", (long) keyval, bucketno);
else
printf("hash(%d) = %d\n", keyval, bucketno);
printf("hash(%ld) = %d\n", (long) keyval, bucketno);
#endif
return bucketno;
@ -585,6 +594,9 @@ ExecScanHashBucket(HashJoinState *hjstate,
false); /* do not pfree this tuple */
econtext->ecxt_innertuple = inntuple;
/* reset temp memory each time to avoid leaks from qual expression */
ResetExprContext(econtext);
if (ExecQual(hjclauses, econtext, false))
{
hjstate->hj_CurTuple = hashTuple;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.30 2000/01/26 05:56:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.31 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -51,13 +51,12 @@ ExecHashJoin(HashJoin *node)
List *qual;
ScanDirection dir;
TupleTableSlot *inntuple;
Var *outerVar;
Node *outerVar;
ExprContext *econtext;
HashJoinTable hashtable;
HeapTuple curtuple;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
Var *innerhashkey;
int i;
bool hashPhaseDone;
@ -73,7 +72,6 @@ ExecHashJoin(HashJoin *node)
hashNode = (Hash *) innerPlan(node);
outerNode = outerPlan(node);
hashPhaseDone = node->hashdone;
dir = estate->es_direction;
/* -----------------
@ -81,13 +79,21 @@ ExecHashJoin(HashJoin *node)
* -----------------
*/
hashtable = hjstate->hj_HashTable;
/* --------------------
* initialize expression context
* --------------------
*/
econtext = hjstate->jstate.cs_ExprContext;
/* ----------------
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle.
* ----------------
*/
ResetExprContext(econtext);
/* ----------------
* Check to see if we're still projecting out tuples from a previous
* join tuple (because there is a function-returning-set in the
* projection expressions). If so, try to project another one.
* ----------------
*/
if (hjstate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
@ -96,6 +102,8 @@ ExecHashJoin(HashJoin *node)
result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
/* Done with that source tuple... */
hjstate->jstate.cs_TupFromTlist = false;
}
/* ----------------
@ -112,8 +120,7 @@ ExecHashJoin(HashJoin *node)
*/
hashtable = ExecHashTableCreate(hashNode);
hjstate->hj_HashTable = hashtable;
innerhashkey = hashNode->hashkey;
hjstate->hj_InnerHashKey = innerhashkey;
hjstate->hj_InnerHashKey = hashNode->hashkey;
/* ----------------
* execute the Hash node, to build the hash table
@ -139,7 +146,7 @@ ExecHashJoin(HashJoin *node)
* ----------------
*/
outerTupleSlot = hjstate->jstate.cs_OuterTupleSlot;
outerVar = get_leftop(clause);
outerVar = (Node *) get_leftop(clause);
for (;;)
{
@ -220,6 +227,10 @@ ExecHashJoin(HashJoin *node)
InvalidBuffer,
false); /* don't pfree this tuple */
econtext->ecxt_innertuple = inntuple;
/* reset temp memory each time to avoid leaks from qpqual */
ResetExprContext(econtext);
/* ----------------
* if we pass the qual, then save state for next call and
* have ExecProject form the projection, store it
@ -279,12 +290,9 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
/* ----------------
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &hjstate->jstate, parent);
ExecAssignExprContext(estate, &hjstate->jstate);
#define HASHJOIN_NSLOTS 2
@ -343,10 +351,10 @@ ExecInitHashJoin(HashJoin *node, EState *estate, Plan *parent)
hjstate->hj_HashTable = (HashJoinTable) NULL;
hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
hjstate->hj_InnerHashKey = (Var *) NULL;
hjstate->hj_InnerHashKey = (Node *) NULL;
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
hjstate->jstate.cs_TupFromTlist = (bool) false;
hjstate->jstate.cs_TupFromTlist = false;
return TRUE;
}
@ -396,6 +404,7 @@ ExecEndHashJoin(HashJoin *node)
* ----------------
*/
ExecFreeProjectionInfo(&hjstate->jstate);
ExecFreeExprContext(&hjstate->jstate);
/* ----------------
* clean up subtrees
@ -510,7 +519,7 @@ ExecHashJoinNewBatch(HashJoinState *hjstate)
BufFile *innerFile;
TupleTableSlot *slot;
ExprContext *econtext;
Var *innerhashkey;
Node *innerhashkey;
if (newbatch > 1)
{
@ -651,10 +660,10 @@ ExecReScanHashJoin(HashJoin *node, ExprContext *exprCtxt, Plan *parent)
hjstate->hj_CurBucketNo = 0;
hjstate->hj_CurTuple = (HashJoinTuple) NULL;
hjstate->hj_InnerHashKey = (Var *) NULL;
hjstate->hj_InnerHashKey = (Node *) NULL;
hjstate->jstate.cs_OuterTupleSlot = (TupleTableSlot *) NULL;
hjstate->jstate.cs_TupFromTlist = (bool) false;
hjstate->jstate.cs_TupFromTlist = false;
/*
* if chgParam of subnodes is not null then plans will be re-scanned

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.51 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.52 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -79,6 +79,7 @@ IndexNext(IndexScan *node)
EState *estate;
CommonScanState *scanstate;
IndexScanState *indexstate;
ExprContext *econtext;
ScanDirection direction;
Snapshot snapshot;
IndexScanDescPtr scanDescs;
@ -89,7 +90,6 @@ IndexNext(IndexScan *node)
TupleTableSlot *slot;
Buffer buffer = InvalidBuffer;
int numIndices;
bool bBackward;
int indexNumber;
@ -112,6 +112,7 @@ IndexNext(IndexScan *node)
scanDescs = indexstate->iss_ScanDescs;
heapRelation = scanstate->css_currentRelation;
numIndices = indexstate->iss_NumIndices;
econtext = scanstate->cstate.cs_ExprContext;
slot = scanstate->css_ScanTupleSlot;
/*
@ -133,14 +134,15 @@ IndexNext(IndexScan *node)
slot->val = estate->es_evTuple[node->scan.scanrelid - 1];
slot->ttc_shouldFree = false;
scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
econtext->ecxt_scantuple = slot;
/* Does the tuple meet any of the OR'd indxqual conditions? */
ResetExprContext(econtext);
foreach(qual, node->indxqualorig)
{
if (ExecQual((List *) lfirst(qual),
scanstate->cstate.cs_ExprContext,
false))
if (ExecQual((List *) lfirst(qual), econtext, false))
break;
}
if (qual == NIL) /* would not be returned by indices */
@ -219,14 +221,13 @@ IndexNext(IndexScan *node)
* and checking for failure with all previous
* qualifications.
*/
scanstate->cstate.cs_ExprContext->ecxt_scantuple = slot;
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
qual = node->indxqualorig;
for (prev_index = 0; prev_index < indexstate->iss_IndexPtr;
prev_index++)
{
if (ExecQual((List *) lfirst(qual),
scanstate->cstate.cs_ExprContext,
false))
if (ExecQual((List *) lfirst(qual), econtext, false))
{
prev_matches = true;
break;
@ -234,7 +235,7 @@ IndexNext(IndexScan *node)
qual = lnext(qual);
}
if (!prev_matches)
return slot;/* OK to return tuple */
return slot; /* OK to return tuple */
/* Duplicate tuple, so drop it and loop back for another */
ExecClearTuple(slot);
}
@ -284,7 +285,7 @@ ExecIndexScan(IndexScan *node)
* use IndexNext as access method
* ----------------
*/
return ExecScan(&node->scan, IndexNext);
return ExecScan(&node->scan, (ExecScanAccessMtd) IndexNext);
}
/* ----------------------------------------------------------------
@ -293,9 +294,8 @@ ExecIndexScan(IndexScan *node)
* Recalculates the value of the scan keys whose value depends on
* information known at runtime and rescans the indexed relation.
* Updating the scan key was formerly done separately in
* ExecUpdateIndexScanKeys. Integrating it into ReScan
* makes rescans of indices and
* relations/general streams more uniform.
* ExecUpdateIndexScanKeys. Integrating it into ReScan makes
* rescans of indices and relations/general streams more uniform.
*
* ----------------------------------------------------------------
*/
@ -304,6 +304,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
{
EState *estate;
IndexScanState *indexstate;
ExprContext *econtext;
ScanDirection direction;
IndexScanDescPtr scanDescs;
ScanKey *scanKeys;
@ -311,8 +312,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
ScanKey skey;
int numIndices;
int i;
Pointer *runtimeKeyInfo;
int **runtimeKeyInfo;
int *numScanKeys;
List *indxqual;
List *qual;
@ -326,22 +326,34 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
bool isNull;
bool isDone;
indexstate = node->indxstate;
estate = node->scan.plan.state;
indexstate = node->indxstate;
econtext = indexstate->iss_RuntimeContext; /* context for runtime keys */
direction = estate->es_direction;
numIndices = indexstate->iss_NumIndices;
scanDescs = indexstate->iss_ScanDescs;
scanKeys = indexstate->iss_ScanKeys;
runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
numScanKeys = indexstate->iss_NumScanKeys;
indexstate->iss_IndexPtr = -1;
if (ScanDirectionIsBackward(node->indxorderdir))
indexstate->iss_IndexPtr = numIndices;
/* If we are being passed an outer tuple, save it for runtime key calc */
if (exprCtxt != NULL)
node->scan.scanstate->cstate.cs_ExprContext->ecxt_outertuple =
exprCtxt->ecxt_outertuple;
if (econtext)
{
/*
* If we are being passed an outer tuple,
* save it for runtime key calc
*/
if (exprCtxt != NULL)
econtext->ecxt_outertuple = exprCtxt->ecxt_outertuple;
/*
* Reset the runtime-key context so we don't leak memory as
* each outer tuple is scanned. Note this assumes that we
* will recalculate *all* runtime keys on each call.
*/
ResetExprContext(econtext);
}
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
@ -364,7 +376,7 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
if (runtimeKeyInfo)
{
run_keys = (int *) runtimeKeyInfo[i];
run_keys = runtimeKeyInfo[i];
for (j = 0; j < n_keys; j++)
{
@ -373,6 +385,13 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
* expression and evaluate it with respect to the current
* outer tuple. We then stick the result into the scan
* key.
*
* Note: the result of the eval could be a pass-by-ref
* value that's stored in the outer scan's tuple, not in
* econtext->ecxt_per_tuple_memory. We assume that the
* outer tuple will stay put throughout our scan. If this
* is wrong, we could copy the result into our context
* explicitly, but I think that's not necessary...
*/
if (run_keys[j] != NO_OP)
{
@ -385,10 +404,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
* pass in isDone but ignore it. We don't iterate in
* quals
*/
scanvalue = (Datum)
ExecEvalExpr(scanexpr,
node->scan.scanstate->cstate.cs_ExprContext,
&isNull, &isDone);
scanvalue = ExecEvalExprSwitchContext(scanexpr,
econtext,
&isNull,
&isDone);
scan_keys[j].sk_argument = scanvalue;
if (isNull)
scan_keys[j].sk_flags |= SK_ISNULL;
@ -401,11 +420,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent)
skey = scanKeys[i];
index_rescan(scan, direction, skey);
}
/* ----------------
* perhaps return something meaningful
* ----------------
*/
return;
}
/* ----------------------------------------------------------------
@ -421,7 +435,7 @@ ExecEndIndexScan(IndexScan *node)
{
CommonScanState *scanstate;
IndexScanState *indexstate;
Pointer *runtimeKeyInfo;
int **runtimeKeyInfo;
ScanKey *scanKeys;
List *indxqual;
int *numScanKeys;
@ -431,7 +445,7 @@ ExecEndIndexScan(IndexScan *node)
scanstate = node->scan.scanstate;
indexstate = node->indxstate;
indxqual = node->indxqual;
runtimeKeyInfo = (Pointer *) indexstate->iss_RuntimeKeyInfo;
runtimeKeyInfo = indexstate->iss_RuntimeKeyInfo;
/* ----------------
* extract information from the node
@ -451,6 +465,9 @@ ExecEndIndexScan(IndexScan *node)
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
ExecFreeExprContext(&scanstate->cstate);
if (indexstate->iss_RuntimeContext)
FreeExprContext(indexstate->iss_RuntimeContext);
/* ----------------
* close the heap and index relations
@ -474,12 +491,7 @@ ExecEndIndexScan(IndexScan *node)
{
for (i = 0; i < numIndices; i++)
{
List *qual;
int n_keys;
qual = nth(i, indxqual);
n_keys = length(qual);
if (n_keys > 0)
if (runtimeKeyInfo[i] != NULL)
pfree(runtimeKeyInfo[i]);
}
pfree(runtimeKeyInfo);
@ -491,7 +503,6 @@ ExecEndIndexScan(IndexScan *node)
*/
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
ExecClearTuple(scanstate->css_ScanTupleSlot);
/* ExecClearTuple(scanstate->css_RawTupleSlot); */
}
/* ----------------------------------------------------------------
@ -562,7 +573,7 @@ ExecIndexRestrPos(IndexScan *node)
*
* old comments
* Creates the run-time state information for the node and
* sets the relation id to contain relevant decriptors.
* sets the relation id to contain relevant descriptors.
*
* Parameters:
* node: IndexNode node produced by the planner.
@ -583,19 +594,16 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
int *numScanKeys;
RelationPtr relationDescs;
IndexScanDescPtr scanDescs;
Pointer *runtimeKeyInfo;
int **runtimeKeyInfo;
bool have_runtime_keys;
List *rangeTable;
RangeTblEntry *rtentry;
Index relid;
Oid reloid;
Relation currentRelation;
HeapScanDesc currentScanDesc;
ScanDirection direction;
int baseid;
List *execParam = NULL;
List *execParam = NIL;
/* ----------------
* assign execution state to node
@ -610,25 +618,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
* --------------------------------
*/
scanstate = makeNode(CommonScanState);
/*
scanstate->ss_ProcOuterFlag = false;
scanstate->ss_OldRelId = 0;
*/
node->scan.scanstate = scanstate;
/* ----------------
* assign node's base_id .. we don't use AssignNodeBaseid() because
* the increment is done later on after we assign the index scan's
* scanstate. see below.
* ----------------
*/
baseid = estate->es_BaseId;
/* scanstate->csstate.cstate.bnode.base_id = baseid; */
scanstate->cstate.cs_base_id = baseid;
/* ----------------
* create expression context for node
* Miscellaneous initialization
*
* + create expression context for node
* ----------------
*/
ExecAssignExprContext(estate, &scanstate->cstate);
@ -640,7 +635,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
/* ExecInitRawTupleSlot(estate, scanstate); */
/* ----------------
* initialize projection info. result type comes from scan desc
@ -661,19 +655,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
indexstate->iss_ScanKeys = NULL;
indexstate->iss_NumScanKeys = NULL;
indexstate->iss_RuntimeKeyInfo = NULL;
indexstate->iss_RuntimeContext = NULL;
indexstate->iss_RelationDescs = NULL;
indexstate->iss_ScanDescs = NULL;
node->indxstate = indexstate;
/* ----------------
* assign base id to index scan state also
* ----------------
*/
indexstate->cstate.cs_base_id = baseid;
baseid++;
estate->es_BaseId = baseid;
/* ----------------
* get the index node information
* ----------------
@ -696,12 +683,11 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
scanDescs = (IndexScanDescPtr) palloc(numIndices * sizeof(IndexScanDesc));
/* ----------------
* initialize runtime key info.
* initialize space for runtime key info (may not be needed)
* ----------------
*/
have_runtime_keys = false;
runtimeKeyInfo = (Pointer *)
palloc(numIndices * sizeof(Pointer));
runtimeKeyInfo = (int **) palloc(numIndices * sizeof(int *));
/* ----------------
* build the index scan keys from the index qualification
@ -719,9 +705,9 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
qual = lfirst(indxqual);
indxqual = lnext(indxqual);
n_keys = length(qual);
scan_keys = (n_keys <= 0) ? NULL :
scan_keys = (n_keys <= 0) ? (ScanKey) NULL :
(ScanKey) palloc(n_keys * sizeof(ScanKeyData));
run_keys = (n_keys <= 0) ? NULL :
run_keys = (n_keys <= 0) ? (int *) NULL :
(int *) palloc(n_keys * sizeof(int));
CXT1_printf("ExecInitIndexScan: context is %d\n", CurrentMemoryContext);
@ -966,12 +952,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
}
/* ----------------
* store the key information into our array.
* store the key information into our arrays.
* ----------------
*/
numScanKeys[i] = n_keys;
scanKeys[i] = scan_keys;
runtimeKeyInfo[i] = (Pointer) run_keys;
runtimeKeyInfo[i] = run_keys;
}
indexstate->iss_NumIndices = numIndices;
@ -988,12 +974,35 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
* (one for each index) to arrays of flags (one for each key)
* which indicate that the qual needs to be evaluated at runtime.
* -cim 10/24/89
*
* If we do have runtime keys, we need an ExprContext to evaluate them;
* the node's standard context won't do because we want to reset that
* context for every tuple. So, build another context just like the
* other one...
* -tgl 7/11/00
* ----------------
*/
if (have_runtime_keys)
indexstate->iss_RuntimeKeyInfo = (Pointer) runtimeKeyInfo;
{
ExprContext *stdecontext = scanstate->cstate.cs_ExprContext;
ExecAssignExprContext(estate, &scanstate->cstate);
indexstate->iss_RuntimeKeyInfo = runtimeKeyInfo;
indexstate->iss_RuntimeContext = scanstate->cstate.cs_ExprContext;
scanstate->cstate.cs_ExprContext = stdecontext;
}
else
{
indexstate->iss_RuntimeKeyInfo = NULL;
indexstate->iss_RuntimeContext = NULL;
/* Get rid of the speculatively-allocated flag arrays, too */
for (i = 0; i < numIndices; i++)
{
if (runtimeKeyInfo[i] != NULL)
pfree(runtimeKeyInfo[i]);
}
pfree(runtimeKeyInfo);
}
/* ----------------
* get the range table and direction information
@ -1026,7 +1035,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
/* ----------------
* get the scan type from the relation descriptor.
* ----------------
@ -1034,12 +1042,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
/* ----------------
* index scans don't have subtrees..
* ----------------
*/
/* scanstate->ss_ProcOuterFlag = false; */
/* ----------------
* open the index relations and initialize
* relation and scan descriptors.
@ -1066,10 +1068,8 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent)
indexstate->iss_RelationDescs = relationDescs;
indexstate->iss_ScanDescs = scanDescs;
indexstate->cstate.cs_TupFromTlist = false;
/*
* if there are some PARAM_EXEC in skankeys then force index rescan on
* if there are some PARAM_EXEC in scankeys then force index rescan on
* first scan.
*/
((Plan *) node)->chgParam = execParam;

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.31 2000/06/18 22:44:03 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.32 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -158,17 +158,12 @@ ExecInitMaterial(Material *node, EState *estate, Plan *parent)
node->matstate = matstate;
/* ----------------
* Miscellanious initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + assign result tuple slot
* Miscellaneous initialization
*
* Materialization nodes don't need ExprContexts because
* they never call ExecQual or ExecTargetList.
* they never call ExecQual or ExecProject.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &matstate->csstate.cstate, parent);
#define MATERIAL_NSLOTS 1
/* ----------------

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.35 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.36 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -202,45 +202,53 @@ MJFormSkipQual(List *qualList, char *replaceopname)
static bool
MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
{
bool result;
MemoryContext oldContext;
List *clause;
List *eqclause;
Datum const_value;
bool isNull;
bool isDone;
/* ----------------
* if we have no compare qualification, return nil
* ----------------
/*
* Do expression eval in short-lived context.
*/
if (compareQual == NIL)
return false;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/* ----------------
* for each pair of clauses, test them until
* our compare conditions are satisfied
* our compare conditions are satisfied.
* if we reach the end of the list, none of our key greater-than
* conditions were satisfied so we return false.
* ----------------
*/
result = false; /* assume 'false' result */
eqclause = eqQual;
foreach(clause, compareQual)
{
Datum const_value;
bool isNull;
bool isDone;
/* ----------------
* first test if our compare clause is satisfied.
* if so then return true. ignore isDone, don't iterate in
* quals.
* if so then return true.
*
* A NULL result is considered false.
* ignore isDone, don't iterate in quals.
* ----------------
*/
const_value = (Datum)
ExecEvalExpr((Node *) lfirst(clause), econtext, &isNull, &isDone);
const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
&isNull, &isDone);
if (DatumGetInt32(const_value) != 0)
return true;
if (DatumGetBool(const_value) && !isNull)
{
result = true;
break;
}
/* ----------------
* ok, the compare clause failed so we test if the keys
* are equal... if key1 != key2, we return false.
* otherwise key1 = key2 so we move on to the next pair of keys.
*
* ignore isDone, don't iterate in quals.
* ----------------
*/
const_value = ExecEvalExpr((Node *) lfirst(eqclause),
@ -248,17 +256,15 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
&isNull,
&isDone);
if (DatumGetInt32(const_value) == 0)
return false;
if (! DatumGetBool(const_value) || isNull)
break; /* return false */
eqclause = lnext(eqclause);
}
/* ----------------
* if we get here then it means none of our key greater-than
* conditions were satisfied so we return false.
* ----------------
*/
return false;
MemoryContextSwitchTo(oldContext);
return result;
}
/* ----------------------------------------------------------------
@ -403,24 +409,18 @@ ExecMergeJoin(MergeJoin *node)
List *qual;
bool qualResult;
bool compareResult;
Plan *innerPlan;
TupleTableSlot *innerTupleSlot;
Plan *outerPlan;
TupleTableSlot *outerTupleSlot;
ExprContext *econtext;
#ifdef ENABLE_OUTER_JOINS
/*
* These should be set from the expression context! - thomas
* 1999-02-20
*/
static bool isLeftJoin = true;
static bool isRightJoin = false;
#endif
/* ----------------
@ -448,20 +448,34 @@ ExecMergeJoin(MergeJoin *node)
}
/* ----------------
* ok, everything is setup.. let's go to work
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle.
* ----------------
*/
ResetExprContext(econtext);
/* ----------------
* Check to see if we're still projecting out tuples from a previous
* join tuple (because there is a function-returning-set in the
* projection expressions). If so, try to project another one.
* ----------------
*/
if (mergestate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
ProjectionInfo *projInfo;
bool isDone;
projInfo = mergestate->jstate.cs_ProjInfo;
result = ExecProject(projInfo, &isDone);
result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
/* Done with that source tuple... */
mergestate->jstate.cs_TupFromTlist = false;
}
/* ----------------
* ok, everything is setup.. let's go to work
* ----------------
*/
for (;;)
{
/* ----------------
@ -547,6 +561,8 @@ ExecMergeJoin(MergeJoin *node)
case EXEC_MJ_JOINTEST:
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
ResetExprContext(econtext);
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
@ -565,6 +581,14 @@ ExecMergeJoin(MergeJoin *node)
MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
/*
* Check the qpqual to see if we actually want to return
* this join tuple. If not, can proceed with merge.
*
* (We don't bother with a ResetExprContext here, on the
* assumption that we just did one before checking the merge
* qual. One per tuple should be sufficient.)
*/
qualResult = ExecQual((List *) qual, econtext, false);
MJ_DEBUG_QUAL(qual, qualResult);
@ -693,6 +717,8 @@ ExecMergeJoin(MergeJoin *node)
innerTupleSlot = econtext->ecxt_innertuple;
econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
ResetExprContext(econtext);
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
@ -709,11 +735,7 @@ ExecMergeJoin(MergeJoin *node)
*/
ExecRestrPos(innerPlan);
#if 0
mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
#endif
mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
}
else
{
@ -777,6 +799,8 @@ ExecMergeJoin(MergeJoin *node)
* we update the marked tuple and go join them.
* ----------------
*/
ResetExprContext(econtext);
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
@ -886,6 +910,8 @@ ExecMergeJoin(MergeJoin *node)
* we update the marked tuple and go join them.
* ----------------
*/
ResetExprContext(econtext);
qualResult = ExecQual((List *) mergeclauses, econtext, false);
MJ_DEBUG_QUAL(mergeclauses, qualResult);
@ -1142,12 +1168,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
/* ----------------
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &mergestate->jstate, parent);
ExecAssignExprContext(estate, &mergestate->jstate);
#define MERGEJOIN_NSLOTS 2
@ -1251,6 +1274,7 @@ ExecEndMergeJoin(MergeJoin *node)
* ----------------
*/
ExecFreeProjectionInfo(&mergestate->jstate);
ExecFreeExprContext(&mergestate->jstate);
/* ----------------
* shut down the subplans

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.16 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.17 2000/07/12 02:37:03 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,18 +32,18 @@
*
* It scans the inner relation to join with current outer tuple.
*
* If none is found, next tuple form the outer relation is retrieved
* If none is found, next tuple from the outer relation is retrieved
* and the inner relation is scanned from the beginning again to join
* with the outer tuple.
*
* Nil is returned if all the remaining outer tuples are tried and
* NULL is returned if all the remaining outer tuples are tried and
* all fail to join with the inner tuples.
*
* Nil is also returned if there is no tuple from inner realtion.
* NULL is also returned if there is no tuple from inner relation.
*
* Conditions:
* -- outerTuple contains current tuple from outer relation and
* the right son(inner realtion) maintains "cursor" at the tuple
* the right son(inner relation) maintains "cursor" at the tuple
* returned previously.
* This is achieved by maintaining a scan position on the outer
* relation.
@ -60,10 +60,8 @@ ExecNestLoop(NestLoop *node, Plan *parent)
Plan *innerPlan;
Plan *outerPlan;
bool needNewOuterTuple;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
List *qual;
ExprContext *econtext;
@ -77,11 +75,6 @@ ExecNestLoop(NestLoop *node, Plan *parent)
qual = node->join.qual;
outerPlan = outerPlan(&node->join);
innerPlan = innerPlan(&node->join);
/* ----------------
* initialize expression context
* ----------------
*/
econtext = nlstate->jstate.cs_ExprContext;
/* ----------------
@ -92,11 +85,18 @@ ExecNestLoop(NestLoop *node, Plan *parent)
econtext->ecxt_outertuple = outerTupleSlot;
/* ----------------
* Ok, everything is setup for the join so now loop until
* we return a qualifying join tuple..
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle.
* ----------------
*/
ResetExprContext(econtext);
/* ----------------
* Check to see if we're still projecting out tuples from a previous
* join tuple (because there is a function-returning-set in the
* projection expressions). If so, try to project another one.
* ----------------
*/
if (nlstate->jstate.cs_TupFromTlist)
{
TupleTableSlot *result;
@ -105,9 +105,17 @@ ExecNestLoop(NestLoop *node, Plan *parent)
result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone);
if (!isDone)
return result;
/* Done with that source tuple... */
nlstate->jstate.cs_TupFromTlist = false;
}
/* ----------------
* Ok, everything is setup for the join so now loop until
* we return a qualifying join tuple..
* ----------------
*/
ENL1_printf("entering main loop");
for (;;)
{
/* ----------------
@ -115,15 +123,7 @@ ExecNestLoop(NestLoop *node, Plan *parent)
* and join it with the current outer tuple.
* ----------------
*/
needNewOuterTuple = false;
if (!TupIsNull(outerTupleSlot))
ENL1_printf("have outer tuple, deal with it");
else
{
ENL1_printf("outer tuple is nil, need new outer tuple");
needNewOuterTuple = true;
}
needNewOuterTuple = TupIsNull(outerTupleSlot);
/* ----------------
* if we have an outerTuple, try to get the next inner tuple.
@ -229,9 +229,11 @@ ExecNestLoop(NestLoop *node, Plan *parent)
}
/* ----------------
* qualification failed so we have to try again..
* Tuple fails qual, so free per-tuple memory and try again.
* ----------------
*/
ResetExprContext(econtext);
ENL1_printf("qualification failed, looping");
}
}
@ -263,18 +265,14 @@ ExecInitNestLoop(NestLoop *node, EState *estate, Plan *parent)
* ----------------
*/
nlstate = makeNode(NestLoopState);
nlstate->nl_PortalFlag = false;
node->nlstate = nlstate;
/* ----------------
* Miscellanious initialization
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &nlstate->jstate, parent);
ExecAssignExprContext(estate, &nlstate->jstate);
#define NESTLOOP_NSLOTS 1
@ -348,6 +346,7 @@ ExecEndNestLoop(NestLoop *node)
* ----------------
*/
ExecFreeProjectionInfo(&nlstate->jstate);
ExecFreeExprContext(&nlstate->jstate);
/* ----------------
* close down subplans
@ -386,9 +385,7 @@ ExecReScanNestLoop(NestLoop *node, ExprContext *exprCtxt, Plan *parent)
if (outerPlan->chgParam == NULL)
ExecReScan(outerPlan, exprCtxt, (Plan *) node);
/* let outerPlan to free its result typle ... */
/* let outerPlan to free its result tuple ... */
nlstate->jstate.cs_OuterTupleSlot = NULL;
nlstate->jstate.cs_TupFromTlist = false;
return;
}

View File

@ -3,21 +3,18 @@
* nodeResult.c
* support for constant nodes needing special code.
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* DESCRIPTION
*
* Example: in constant queries where no relations are scanned,
* the planner generates result nodes. Examples of such queries are:
* Result nodes are used in queries where no relations are scanned.
* Examples of such queries are:
*
* retrieve (x = 1)
* and
* append emp (name = "mike", salary = 15000)
*
* Result nodes are also used to optimise queries
* with tautological qualifications like:
* Result nodes are also used to optimise queries with constant
* qualifications (ie, quals that do not depend on the scanned data),
* such as:
*
* retrieve (emp.all) where 2 > 1
*
@ -27,13 +24,22 @@
* /
* SeqScan (emp.all)
*
* At runtime, the Result node evaluates the constant qual once.
* If it's false, we can return an empty result set without running
* the controlled plan at all. If it's true, we run the controlled
* plan normally and pass back the results.
*
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.13 2000/01/26 05:56:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.14 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "postgres.h"
#include "executor/executor.h"
#include "executor/nodeResult.h"
@ -41,7 +47,7 @@
/* ----------------------------------------------------------------
* ExecResult(node)
*
* returns the tuples from the outer plan which satisify the
* returns the tuples from the outer plan which satisfy the
* qualification clause. Since result nodes with right
* subtrees are never planned, we ignore the right subtree
* entirely (for now).. -cim 10/7/89
@ -67,15 +73,17 @@ ExecResult(Result *node)
* ----------------
*/
resstate = node->resstate;
/* ----------------
* get the expression context
* ----------------
*/
econtext = resstate->cstate.cs_ExprContext;
/* ----------------
* check tautological qualifications like (2 > 1)
* Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle.
* ----------------
*/
ResetExprContext(econtext);
/* ----------------
* check constant qualifications like (2 > 1), if not already done
* ----------------
*/
if (resstate->rs_checkqual)
@ -92,74 +100,64 @@ ExecResult(Result *node)
}
}
/* ----------------
* Check to see if we're still projecting out tuples from a previous
* scan tuple (because there is a function-returning-set in the
* projection expressions). If so, try to project another one.
* ----------------
*/
if (resstate->cstate.cs_TupFromTlist)
{
ProjectionInfo *projInfo;
projInfo = resstate->cstate.cs_ProjInfo;
resultSlot = ExecProject(projInfo, &isDone);
resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone);
if (!isDone)
return resultSlot;
/* Done with that source tuple... */
resstate->cstate.cs_TupFromTlist = false;
}
/* ----------------
* retrieve a tuple that satisfy the qual from the outer plan until
* there are no more.
*
* if rs_done is 1 then it means that we were asked to return
* a constant tuple and we alread did the last time ExecResult()
* was called, so now we are through.
* if rs_done is true then it means that we were asked to return
* a constant tuple and we already did the last time ExecResult()
* was called, OR that we failed the constant qual check.
* Either way, now we are through.
* ----------------
*/
outerPlan = outerPlan(node);
while (!resstate->rs_done)
if (!resstate->rs_done)
{
outerPlan = outerPlan(node);
/* ----------------
* get next outer tuple if necessary.
* ----------------
*/
if (outerPlan != NULL)
{
/* ----------------
* retrieve tuples from the outer plan until there are no more.
* ----------------
*/
outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
if (TupIsNull(outerTupleSlot))
return NULL;
resstate->cstate.cs_OuterTupleSlot = outerTupleSlot;
/* ----------------
* XXX gross hack. use outer tuple as scan tuple for projection
* ----------------
*/
econtext->ecxt_outertuple = outerTupleSlot;
econtext->ecxt_scantuple = outerTupleSlot;
}
else
{
/* ----------------
* if we don't have an outer plan, then it's probably
* the case that we are doing a retrieve or an append
* with a constant target list, so we should only return
* the constant tuple once or never if we fail the qual.
* if we don't have an outer plan, then we are just generating
* the results from a constant target list. Do it only once.
* ----------------
*/
resstate->rs_done = 1;
resstate->rs_done = true;
}
/* ----------------
* get the information to place into the expr context
* ----------------
*/
resstate = node->resstate;
outerTupleSlot = resstate->cstate.cs_OuterTupleSlot;
/* ----------------
* fill in the information in the expression context
* XXX gross hack. use outer tuple as scan tuple
* ----------------
*/
econtext->ecxt_outertuple = outerTupleSlot;
econtext->ecxt_scantuple = outerTupleSlot;
/* ----------------
* form the result tuple and pass it back using ExecProject()
* form the result tuple using ExecProject(), and return it.
* ----------------
*/
projInfo = resstate->cstate.cs_ProjInfo;
@ -200,14 +198,11 @@ ExecInitResult(Result *node, EState *estate, Plan *parent)
node->resstate = resstate;
/* ----------------
* Miscellanious initialization
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &resstate->cstate, parent);
ExecAssignExprContext(estate, &resstate->cstate);
#define RESULT_NSLOTS 1
@ -247,7 +242,7 @@ ExecCountSlotsResult(Result *node)
/* ----------------------------------------------------------------
* ExecEndResult
*
* fees up storage allocated through C routines
* frees up storage allocated through C routines
* ----------------------------------------------------------------
*/
void
@ -266,9 +261,8 @@ ExecEndResult(Result *node)
* is freed at end-transaction time. -cim 6/2/91
* ----------------
*/
ExecFreeExprContext(&resstate->cstate); /* XXX - new for us - er1p */
ExecFreeTypeInfo(&resstate->cstate); /* XXX - new for us - er1p */
ExecFreeProjectionInfo(&resstate->cstate);
ExecFreeExprContext(&resstate->cstate);
/* ----------------
* shut down subplans
@ -301,5 +295,4 @@ ExecReScanResult(Result *node, ExprContext *exprCtxt, Plan *parent)
if (((Plan *) node)->lefttree &&
((Plan *) node)->lefttree->chgParam == NULL)
ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.23 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.24 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -31,8 +31,7 @@
#include "parser/parsetree.h"
static Oid InitScanRelation(SeqScan *node, EState *estate,
CommonScanState *scanstate, Plan *outerPlan);
CommonScanState *scanstate);
static TupleTableSlot *SeqNext(SeqScan *node);
/* ----------------------------------------------------------------
@ -132,25 +131,11 @@ SeqNext(SeqScan *node)
TupleTableSlot *
ExecSeqScan(SeqScan *node)
{
TupleTableSlot *slot;
Plan *outerPlan;
S_printf("ExecSeqScan: scanning node: ");
S_nodeDisplay(node);
/* ----------------
* if there is an outer subplan, get a tuple from it
* else, scan the relation
* use SeqNext as access method
* ----------------
*/
if ((outerPlan = outerPlan((Plan *) node)) != NULL)
slot = ExecProcNode(outerPlan, (Plan *) node);
else
slot = ExecScan(node, SeqNext);
S1_printf("ExecSeqScan: returned tuple slot: %d\n", slot);
return slot;
return ExecScan(node, (ExecScanAccessMtd) SeqNext);
}
/* ----------------------------------------------------------------
@ -162,7 +147,7 @@ ExecSeqScan(SeqScan *node)
*/
static Oid
InitScanRelation(SeqScan *node, EState *estate,
CommonScanState *scanstate, Plan *outerPlan)
CommonScanState *scanstate)
{
Index relid;
List *rangeTable;
@ -173,84 +158,56 @@ InitScanRelation(SeqScan *node, EState *estate,
HeapScanDesc currentScanDesc;
RelationInfo *resultRelationInfo;
if (outerPlan == NULL)
{
/* ----------------
* if the outer node is nil then we are doing a simple
* sequential scan of a relation...
*
* get the relation object id from the relid'th entry
* in the range table, open that relation and initialize
* the scan state...
* ----------------
*/
relid = node->scanrelid;
rangeTable = estate->es_range_table;
rtentry = rt_fetch(relid, rangeTable);
reloid = rtentry->relid;
direction = estate->es_direction;
resultRelationInfo = estate->es_result_relation_info;
ExecOpenScanR(reloid, /* relation */
0, /* nkeys */
NULL, /* scan key */
0, /* is index */
direction,/* scan direction */
estate->es_snapshot,
&currentRelation, /* return: rel desc */
(Pointer *) &currentScanDesc); /* return: scan desc */
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
ExecAssignScanType(scanstate,
RelationGetDescr(currentRelation));
}
else
{
/* ----------------
* otherwise we are scanning tuples from the
* outer subplan so we initialize the outer plan
* and nullify
* ----------------
*/
ExecInitNode(outerPlan, estate, (Plan *) node);
node->scanrelid = 0;
scanstate->css_currentRelation = NULL;
scanstate->css_currentScanDesc = NULL;
ExecAssignScanType(scanstate, NULL);
reloid = InvalidOid;
}
/* ----------------
* return the relation
* get the relation object id from the relid'th entry
* in the range table, open that relation and initialize
* the scan state...
* ----------------
*/
relid = node->scanrelid;
rangeTable = estate->es_range_table;
rtentry = rt_fetch(relid, rangeTable);
reloid = rtentry->relid;
direction = estate->es_direction;
resultRelationInfo = estate->es_result_relation_info;
ExecOpenScanR(reloid, /* relation */
0, /* nkeys */
NULL, /* scan key */
0, /* is index */
direction, /* scan direction */
estate->es_snapshot,
&currentRelation, /* return: rel desc */
(Pointer *) &currentScanDesc); /* return: scan desc */
scanstate->css_currentRelation = currentRelation;
scanstate->css_currentScanDesc = currentScanDesc;
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
return reloid;
}
/* ----------------------------------------------------------------
* ExecInitSeqScan
*
* old comments
* Creates the run-time state information for the seqscan node
* and sets the relation id to contain relevant descriptors.
*
* If there is a outer subtree (sort), the outer subtree
* is initialized and the relation id is set to the descriptors
* returned by the subtree.
* ----------------------------------------------------------------
*/
bool
ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
{
CommonScanState *scanstate;
Plan *outerPlan;
Oid reloid;
HeapScanDesc scandesc;
/* ----------------
* Once upon a time it was possible to have an outerPlan of a SeqScan,
* but not any more.
* ----------------
*/
Assert(outerPlan((Plan *) node) == NULL);
Assert(innerPlan((Plan *) node) == NULL);
/* ----------------
* assign the node's execution state
* ----------------
@ -265,13 +222,11 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
node->scanstate = scanstate;
/* ----------------
* Miscellanious initialization
* Miscellaneous initialization
*
* + assign node's base_id
* + create expression context for node
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &scanstate->cstate, parent);
ExecAssignExprContext(estate, &scanstate->cstate);
#define SEQSCAN_NSLOTS 3
@ -283,12 +238,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, Plan *parent)
ExecInitScanTupleSlot(estate, scanstate);
/* ----------------
* initialize scan relation or outer subplan
* initialize scan relation
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
reloid = InitScanRelation(node, estate, scanstate, outerPlan);
reloid = InitScanRelation(node, estate, scanstate);
scandesc = scanstate->css_currentScanDesc;
scanstate->cstate.cs_TupFromTlist = false;
@ -315,15 +268,12 @@ ExecCountSlotsSeqScan(SeqScan *node)
* ExecEndSeqScan
*
* frees any storage allocated through C routines.
*| ...and also closes relations and/or shuts down outer subplan
*| -cim 8/14/89
* ----------------------------------------------------------------
*/
void
ExecEndSeqScan(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
/* ----------------
* get information from node
@ -341,6 +291,7 @@ ExecEndSeqScan(SeqScan *node)
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
ExecFreeExprContext(&scanstate->cstate);
/* ----------------
* close scan relation
@ -348,13 +299,6 @@ ExecEndSeqScan(SeqScan *node)
*/
ExecCloseR((Plan *) node);
/* ----------------
* clean up outer subtree (does nothing if there is no outerPlan)
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
ExecEndNode(outerPlan, (Plan *) node);
/* ----------------
* clean out the tuple table
* ----------------
@ -367,6 +311,7 @@ ExecEndSeqScan(SeqScan *node)
* Join Support
* ----------------------------------------------------------------
*/
/* ----------------------------------------------------------------
* ExecSeqReScan
*
@ -378,7 +323,6 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
{
CommonScanState *scanstate;
EState *estate;
Plan *outerPlan;
Relation rel;
HeapScanDesc scan;
ScanDirection direction;
@ -386,28 +330,18 @@ ExecSeqReScan(SeqScan *node, ExprContext *exprCtxt, Plan *parent)
scanstate = node->scanstate;
estate = node->plan.state;
if ((outerPlan = outerPlan((Plan *) node)) != NULL)
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
estate->es_evTuple[node->scanrelid - 1] != NULL)
{
/* we are scanning a subplan */
outerPlan = outerPlan((Plan *) node);
ExecReScan(outerPlan, exprCtxt, parent);
}
else
/* otherwise, we are scanning a relation */
{
/* If this is re-scanning of PlanQual ... */
if (estate->es_evTuple != NULL &&
estate->es_evTuple[node->scanrelid - 1] != NULL)
{
estate->es_evTupleNull[node->scanrelid - 1] = false;
return;
}
rel = scanstate->css_currentRelation;
scan = scanstate->css_currentScanDesc;
direction = estate->es_direction;
scan = ExecReScanR(rel, scan, direction, 0, NULL);
scanstate->css_currentScanDesc = scan;
estate->es_evTupleNull[node->scanrelid - 1] = false;
return;
}
rel = scanstate->css_currentRelation;
scan = scanstate->css_currentScanDesc;
direction = estate->es_direction;
scan = ExecReScanR(rel, scan, direction, 0, NULL);
scanstate->css_currentScanDesc = scan;
}
/* ----------------------------------------------------------------
@ -420,33 +354,11 @@ void
ExecSeqMarkPos(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc scan;
scanstate = node->scanstate;
/* ----------------
* if we are scanning a subplan then propagate
* the ExecMarkPos() request to the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
if (outerPlan)
{
ExecMarkPos(outerPlan);
return;
}
/* ----------------
* otherwise we are scanning a relation so mark the
* position using the access methods..
*
* ----------------
*/
scan = scanstate->css_currentScanDesc;
heap_markpos(scan);
return;
}
/* ----------------------------------------------------------------
@ -459,28 +371,9 @@ void
ExecSeqRestrPos(SeqScan *node)
{
CommonScanState *scanstate;
Plan *outerPlan;
HeapScanDesc scan;
scanstate = node->scanstate;
/* ----------------
* if we are scanning a subplan then propagate
* the ExecRestrPos() request to the subplan
* ----------------
*/
outerPlan = outerPlan((Plan *) node);
if (outerPlan)
{
ExecRestrPos(outerPlan);
return;
}
/* ----------------
* otherwise we are scanning a relation so restore the
* position using the access methods..
* ----------------
*/
scan = scanstate->css_currentScanDesc;
heap_restrpos(scan);
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.28 2000/07/09 04:17:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.29 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -255,14 +255,10 @@ ExecInitSort(Sort *node, EState *estate, Plan *parent)
/* ----------------
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks
*
* Sort nodes don't initialize their ExprContexts because
* they never call ExecQual or ExecTargetList.
* they never call ExecQual or ExecProject.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &sortstate->csstate.cstate, parent);
#define SORT_NSLOTS 1
/* ----------------

View File

@ -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.25 2000/04/12 17:15:10 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.26 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -37,11 +37,19 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
SubLink *sublink = node->sublink;
SubLinkType subLinkType = sublink->subLinkType;
bool useor = sublink->useor;
MemoryContext oldcontext;
TupleTableSlot *slot;
Datum result;
bool isDone;
bool found = false; /* TRUE if got at least one subplan tuple */
List *lst;
/*
* We are probably in a short-lived expression-evaluation context.
* Switch to longer-lived per-query context.
*/
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (node->setParam != NIL)
elog(ERROR, "ExecSubPlan: can't set parent params from subquery");
@ -52,12 +60,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
{
foreach(lst, node->parParam)
{
ParamExecData *prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
ParamExecData *prm;
prm = &(econtext->ecxt_param_exec_vals[lfirsti(lst)]);
Assert(pvar != NIL);
prm->value = ExecEvalExpr((Node *) lfirst(pvar),
econtext,
&(prm->isnull), NULL);
prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar),
econtext,
&(prm->isnull),
&isDone);
if (!isDone)
elog(ERROR, "ExecSubPlan: set values not supported for params");
pvar = lnext(pvar);
}
plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam));
@ -84,7 +96,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
* return NULL. Assuming we get a tuple, we just return its first
* column (there can be only one non-junk column in this case).
*/
result = (Datum) (subLinkType == ALL_SUBLINK ? true : false);
result = BoolGetDatum(subLinkType == ALL_SUBLINK);
*isNull = false;
for (slot = ExecProcNode(plan, plan);
@ -93,12 +105,16 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
{
HeapTuple tup = slot->val;
TupleDesc tdesc = slot->ttc_tupleDescriptor;
Datum rowresult = (Datum) (useor ? false : true);
Datum rowresult = BoolGetDatum(! useor);
bool rownull = false;
int col = 1;
if (subLinkType == EXISTS_SUBLINK)
return (Datum) true;
{
found = true;
result = BoolGetDatum(true);
break;
}
if (subLinkType == EXPR_SUBLINK)
{
@ -172,8 +188,10 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
/*
* Now we can eval the combining operator for this column.
*/
expresult = ExecEvalExpr((Node *) expr, econtext, &expnull,
(bool *) NULL);
expresult = ExecEvalExprSwitchContext((Node *) expr, econtext,
&expnull, &isDone);
if (!isDone)
elog(ERROR, "ExecSubPlan: set values not supported for combining operators");
/*
* Combine the result into the row result as appropriate.
@ -188,9 +206,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
/* combine within row per OR semantics */
if (expnull)
rownull = true;
else if (DatumGetInt32(expresult) != 0)
else if (DatumGetBool(expresult))
{
rowresult = (Datum) true;
rowresult = BoolGetDatum(true);
rownull = false;
break; /* needn't look at any more columns */
}
@ -200,9 +218,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
/* combine within row per AND semantics */
if (expnull)
rownull = true;
else if (DatumGetInt32(expresult) == 0)
else if (! DatumGetBool(expresult))
{
rowresult = (Datum) false;
rowresult = BoolGetDatum(false);
rownull = false;
break; /* needn't look at any more columns */
}
@ -215,9 +233,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
/* combine across rows per OR semantics */
if (rownull)
*isNull = true;
else if (DatumGetInt32(rowresult) != 0)
else if (DatumGetBool(rowresult))
{
result = (Datum) true;
result = BoolGetDatum(true);
*isNull = false;
break; /* needn't look at any more rows */
}
@ -227,9 +245,9 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
/* combine across rows per AND semantics */
if (rownull)
*isNull = true;
else if (DatumGetInt32(rowresult) == 0)
else if (! DatumGetBool(rowresult))
{
result = (Datum) false;
result = BoolGetDatum(false);
*isNull = false;
break; /* needn't look at any more rows */
}
@ -252,11 +270,13 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull)
*/
if (subLinkType == EXPR_SUBLINK || subLinkType == MULTIEXPR_SUBLINK)
{
result = (Datum) false;
result = (Datum) 0;
*isNull = true;
}
}
MemoryContextSwitchTo(oldcontext);
return result;
}
@ -277,13 +297,13 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
ExecCreateTupleTable(ExecCountSlotsNode(node->plan) + 10);
sp_estate->es_snapshot = estate->es_snapshot;
node->shutdown = false;
node->needShutdown = false;
node->curTuple = NULL;
if (!ExecInitNode(node->plan, sp_estate, NULL))
return false;
node->shutdown = true; /* now we need to shutdown the subplan */
node->needShutdown = true; /* now we need to shutdown the subplan */
/*
* If this plan is un-correlated or undirect correlated one and want
@ -317,14 +337,21 @@ ExecInitSubPlan(SubPlan *node, EState *estate, Plan *parent)
* ----------------------------------------------------------------
*/
void
ExecSetParamPlan(SubPlan *node)
ExecSetParamPlan(SubPlan *node, ExprContext *econtext)
{
Plan *plan = node->plan;
SubLink *sublink = node->sublink;
MemoryContext oldcontext;
TupleTableSlot *slot;
List *lst;
bool found = false;
/*
* We are probably in a short-lived expression-evaluation context.
* Switch to longer-lived per-query context.
*/
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (sublink->subLinkType == ANY_SUBLINK ||
sublink->subLinkType == ALL_SUBLINK)
elog(ERROR, "ExecSetParamPlan: ANY/ALL subselect unsupported");
@ -345,7 +372,7 @@ ExecSetParamPlan(SubPlan *node)
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
prm->value = (Datum) true;
prm->value = BoolGetDatum(true);
prm->isnull = false;
found = true;
break;
@ -386,7 +413,7 @@ ExecSetParamPlan(SubPlan *node)
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(node->setParam)]);
prm->execPlan = NULL;
prm->value = (Datum) false;
prm->value = BoolGetDatum(false);
prm->isnull = false;
}
else
@ -396,16 +423,18 @@ ExecSetParamPlan(SubPlan *node)
ParamExecData *prm = &(plan->state->es_param_exec_vals[lfirsti(lst)]);
prm->execPlan = NULL;
prm->value = (Datum) NULL;
prm->value = (Datum) 0;
prm->isnull = true;
}
}
}
MemoryContextSwitchTo(oldcontext);
if (plan->extParam == NULL) /* un-correlated ... */
{
ExecEndNode(plan, plan);
node->shutdown = false;
node->needShutdown = false;
}
}
@ -416,10 +445,10 @@ ExecSetParamPlan(SubPlan *node)
void
ExecEndSubPlan(SubPlan *node)
{
if (node->shutdown)
if (node->needShutdown)
{
ExecEndNode(node->plan, node->plan);
node->shutdown = false;
node->needShutdown = false;
}
if (node->curTuple)
{

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.9 2000/06/15 04:09:52 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.10 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -38,12 +38,16 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList)
List *lst;
ItemPointer itemptr;
bool isNull;
bool isDone;
int numTids = 0;
foreach(lst, evalList)
{
itemptr = (ItemPointer) ExecEvalExpr(lfirst(lst), econtext,
&isNull, (bool *) 0);
itemptr = (ItemPointer)
DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst),
econtext,
&isNull,
&isDone));
if (itemptr && ItemPointerIsValid(itemptr))
{
tidList[numTids] = itemptr;
@ -243,7 +247,7 @@ ExecTidScan(TidScan *node)
* use TidNext as access method
* ----------------
*/
return ExecScan(&node->scan, TidNext);
return ExecScan(&node->scan, (ExecScanAccessMtd) TidNext);
}
/* ----------------------------------------------------------------
@ -319,6 +323,7 @@ ExecEndTidScan(TidScan *node)
* ----------------
*/
ExecFreeProjectionInfo(&scanstate->cstate);
ExecFreeExprContext(&scanstate->cstate);
/* ----------------
* close the heap and tid relations
@ -332,7 +337,6 @@ ExecEndTidScan(TidScan *node)
*/
ExecClearTuple(scanstate->cstate.cs_ResultTupleSlot);
ExecClearTuple(scanstate->css_ScanTupleSlot);
/* ExecClearTuple(scanstate->css_RawTupleSlot); */
}
/* ----------------------------------------------------------------
@ -394,11 +398,8 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
RangeTblEntry *rtentry;
Oid relid;
Oid reloid;
Relation currentRelation;
int baseid;
List *execParam = NULL;
List *execParam = NIL;
/* ----------------
* assign execution state to node
@ -413,25 +414,12 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
* --------------------------------
*/
scanstate = makeNode(CommonScanState);
/*
scanstate->ss_ProcOuterFlag = false;
scanstate->ss_OldRelId = 0;
*/
node->scan.scanstate = scanstate;
/* ----------------
* assign node's base_id .. we don't use AssignNodeBaseid() because
* the increment is done later on after we assign the tid scan's
* scanstate. see below.
* ----------------
*/
baseid = estate->es_BaseId;
/* scanstate->csstate.cstate.bnode.base_id = baseid; */
scanstate->cstate.cs_base_id = baseid;
/* ----------------
* create expression context for node
* Miscellaneous initialization
*
* + create expression context for node
* ----------------
*/
ExecAssignExprContext(estate, &scanstate->cstate);
@ -443,7 +431,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
*/
ExecInitResultTupleSlot(estate, &scanstate->cstate);
ExecInitScanTupleSlot(estate, scanstate);
/* ExecInitRawTupleSlot(estate, scanstate); */
/* ----------------
* initialize projection info. result type comes from scan desc
@ -461,14 +448,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
tidstate = makeNode(TidScanState);
node->tidstate = tidstate;
/* ----------------
* assign base id to tid scan state also
* ----------------
*/
tidstate->cstate.cs_base_id = baseid;
baseid++;
estate->es_BaseId = baseid;
/* ----------------
* get the tid node information
* ----------------
@ -514,14 +493,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent)
ExecAssignScanType(scanstate, RelationGetDescr(currentRelation));
ExecAssignResultTypeFromTL((Plan *) node, &scanstate->cstate);
/* ----------------
* tid scans don't have subtrees..
* ----------------
*/
/* scanstate->ss_ProcOuterFlag = false; */
tidstate->cstate.cs_TupFromTlist = false;
/*
* if there are some PARAM_EXEC in skankeys then force tid rescan on
* first scan.

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.29 2000/05/30 00:49:45 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.30 2000/07/12 02:37:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -88,27 +88,32 @@ ExecUnique(Unique *node)
if (!execTuplesMatch(slot->val, uniquestate->priorTuple,
tupDesc,
node->numCols, node->uniqColIdx,
uniquestate->eqfunctions))
uniquestate->eqfunctions,
uniquestate->tempContext))
break;
}
/* ----------------
* We have a new tuple different from the previous saved tuple (if any).
* Save it and return it. Note that we make two copies of the tuple:
* one to keep for our own future comparisons, and one to return to the
* caller. We need to copy the tuple returned by the subplan to avoid
* holding buffer refcounts, and we need our own copy because the caller
* may alter the resultTupleSlot (eg via ExecRemoveJunk).
* Save it and return it. We must copy it because the source subplan
* won't guarantee that this source tuple is still accessible after
* fetching the next source tuple.
*
* Note that we manage the copy ourselves. We can't rely on the result
* tuple slot to maintain the tuple reference because our caller may
* replace the slot contents with a different tuple (see junk filter
* handling in execMain.c). We assume that the caller will no longer
* be interested in the current tuple after he next calls us.
* ----------------
*/
if (uniquestate->priorTuple != NULL)
heap_freetuple(uniquestate->priorTuple);
uniquestate->priorTuple = heap_copytuple(slot->val);
ExecStoreTuple(heap_copytuple(slot->val),
ExecStoreTuple(uniquestate->priorTuple,
resultTupleSlot,
InvalidBuffer,
true);
false); /* tuple does not belong to slot */
return resultTupleSlot;
}
@ -143,14 +148,17 @@ ExecInitUnique(Unique *node, EState *estate, Plan *parent)
/* ----------------
* Miscellaneous initialization
*
* + assign node's base_id
* + assign debugging hooks and
*
* Unique nodes have no ExprContext initialization because
* they never call ExecQual or ExecTargetList.
* they never call ExecQual or ExecProject. But they do need a
* per-tuple memory context anyway for calling execTuplesMatch.
* ----------------
*/
ExecAssignNodeBaseInfo(estate, &uniquestate->cstate, parent);
uniquestate->tempContext =
AllocSetContextCreate(CurrentMemoryContext,
"Unique",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
#define UNIQUE_NSLOTS 1
/* ------------
@ -207,6 +215,8 @@ ExecEndUnique(Unique *node)
ExecEndNode(outerPlan((Plan *) node), (Plan *) node);
MemoryContextDelete(uniquestate->tempContext);
/* clean up tuple table */
ExecClearTuple(uniquestate->cstate.cs_ResultTupleSlot);
if (uniquestate->priorTuple != NULL)