diff options
Diffstat (limited to 'src/backend/executor')
22 files changed, 1078 insertions, 997 deletions
diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index ff3fa0b6ed1..10478f60ad6 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -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; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 6f161d95c0f..3125bf175e9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -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 * @@ -688,13 +691,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); - return (char *) NULL; + 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 * diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 33bfa88734f..fd9d761ffc4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -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 - * as possible. + * 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(). * - * 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. + * This routine is an inner loop routine and must be as fast + * as possible. * ---------------------------------------------------------------- */ 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; - - /* - * here we dispatch the work to the appropriate type of function given - * the type of our expression. - */ + /* Is this still necessary? Doubtful... */ if (expression == NULL) { *isNull = true; - return (Datum) true; + return (Datum) 0; } + /* + * here we dispatch the work to the appropriate type of function given + * the type of our expression. + */ 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; /* @@ -1398,6 +1364,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 * must be FALSE. Also, if we find a NULL result when resultForNull @@ -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) { @@ -1418,14 +1390,6 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) 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]; @@ -1507,6 +1480,11 @@ ExecTargetList(List *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 * fall through and return an empty-but-valid tuple. We do, however, @@ -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; } diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index 98345b408a3..40bbbd916f6 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -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; + econtext = scanstate->cstate.cs_ExprContext; + qual = node->plan.qual; /* ---------------- - * get the expression context + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. * ---------------- */ - econtext = scanstate->cstate.cs_ExprContext; + ResetExprContext(econtext); /* ---------------- - * initialize fields in ExprContext which don't change - * in the course of the scan.. + * 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. * ---------------- */ - qual = node->plan.qual; - econtext->ecxt_relation = scanstate->css_currentRelation; - econtext->ecxt_relid = node->scanrelid; - 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. * ---------------- */ diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index 578b8b80681..37b092fc20f 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -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. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a023405a275..daa40dccf75 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -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,57 +136,104 @@ 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 + * ExecAssignExprContext + * + * This initializes the ExprContext field. It is only necessary + * to do this for nodes which use ExecQual or ExecProject + * because those routines depend on econtext. Other nodes that + * don't have to evaluate expressions don't need to do this. * - * 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. + * Note: we assume CurrentMemoryContext is the correct per-query context. + * This should be true during plan node initialization. * ---------------- */ void -ExecAssignNodeBaseInfo(EState *estate, CommonState *cstate, Plan *parent) +ExecAssignExprContext(EState *estate, CommonState *commonstate) { - int baseId; + ExprContext *econtext = makeNode(ExprContext); - baseId = estate->es_BaseId; - cstate->cs_base_id = baseId; - estate->es_BaseId = baseId + 1; + 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_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; } /* ---------------- - * ExecAssignExprContext + * MakeExprContext * - * 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. + * 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. * ---------------- */ -void -ExecAssignExprContext(EState *estate, CommonState *commonstate) +ExprContext * +MakeExprContext(TupleTableSlot *slot, + MemoryContext queryContext) { - 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_param_exec_vals = estate->es_param_exec_vals; - econtext->ecxt_range_table = estate->es_range_table; /* range table */ + 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; +} - commonstate->cs_ExprContext = 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); } /* ---------------------------------------------------------------- @@ -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 diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 6d8d93a47f1..a92811d0342 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.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; + return (Datum) 0; - valueP = datumCopy(val, - TD->attrs[attrno - 1]->atttypid, - TD->attrs[attrno - 1]->attbyval, - (Size) TD->attrs[attrno - 1]->attlen); - return valueP; + return datumCopy(val, + TD->attrs[attrno - 1]->attbyval, + TD->attrs[attrno - 1]->attlen); } static execution_state * @@ -351,11 +349,19 @@ 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 * CurrentScanCommandId equal to CurrentCommandId. - vadim 08/29/97 @@ -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; } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 0289cf45bd3..1ac5c3c9e21 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -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) + + while (tuplesort_getdatum(peraggstate->sortstate, true, + &newVal, &isNull)) { - Datum oldVal = (Datum) 0; - bool haveOldVal = false; - Datum newVal; - bool isNull; - - tuplesort_performsort(peraggstate->sortstate); - 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,8 +531,7 @@ 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 { @@ -490,6 +539,11 @@ ExecAgg(Agg *node) return NULL; /* + * Clear the per-output-tuple context for each group + */ + MemoryContextReset(aggstate->tup_cxt); + + /* * Initialize working state for a new input tuple group */ for (aggno = 0; aggno < aggstate->numaggs; aggno++) @@ -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,14 +592,27 @@ 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 * be referenced, and these will be the same for all tuples @@ -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]; + + if (! peraggstate->aggref->aggdistinct) + finalize_aggregate(peraggstate, + &aggvalues[aggno], &aggnulls[aggno]); + } + MemoryContextSwitchTo(oldContext); for (aggno = 0; aggno < aggstate->numaggs; aggno++) { AggStatePerAgg peraggstate = &peragg[aggno]; - finalize_aggregate(peraggstate, - &aggvalues[aggno], &aggnulls[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, @@ -628,6 +731,11 @@ ExecAgg(Agg *node) 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); - } } diff --git a/src/backend/executor/nodeAppend.c b/src/backend/executor/nodeAppend.c index 5e34e806e32..2b1eceb15d9 100644 --- a/src/backend/executor/nodeAppend.c +++ b/src/backend/executor/nodeAppend.c @@ -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 * diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index d1ae02616c1..8a445b53d41 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -15,7 +15,7 @@ * locate group boundaries. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.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; } /* diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 34b0a269a5b..9c2d293858c 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -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; /* ---------------- @@ -347,6 +347,14 @@ ExecHashTableCreate(Hash *node) 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; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index d1cb1fe2420..e136c131541 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.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; + econtext = hjstate->jstate.cs_ExprContext; - /* -------------------- - * initialize expression context - * -------------------- + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. + * ---------------- */ - econtext = hjstate->jstate.cs_ExprContext; + 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 diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index be5617d5ef4..0eb3b9f5ae9 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.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,20 +655,13 @@ 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. * ---------------- @@ -1035,12 +1043,6 @@ ExecInitIndexScan(IndexScan *node, EState *estate, Plan *parent) 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; diff --git a/src/backend/executor/nodeMaterial.c b/src/backend/executor/nodeMaterial.c index 1d5c9042489..c0a94066bea 100644 --- a/src/backend/executor/nodeMaterial.c +++ b/src/backend/executor/nodeMaterial.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMaterial.c,v 1.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 /* ---------------- diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 0d522f0d515..a3f92b06901 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.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 diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index ec71ad7ac71..0186e394367 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.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; } diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 5bf132520cb..a1daaf52c4b 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -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; + econtext = resstate->cstate.cs_ExprContext; /* ---------------- - * get the expression context + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. * ---------------- */ - econtext = resstate->cstate.cs_ExprContext; + ResetExprContext(econtext); /* ---------------- - * check tautological qualifications like (2 > 1) + * 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); - } diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 24de6184010..b953dcd3697 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSeqscan.c,v 1.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,85 +158,57 @@ 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, - ¤tRelation, /* return: rel desc */ - (Pointer *) ¤tScanDesc); /* 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, + ¤tRelation, /* return: rel desc */ + (Pointer *) ¤tScanDesc); /* 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 @@ -349,13 +300,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) - { - /* 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) { - /* 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); } diff --git a/src/backend/executor/nodeSort.c b/src/backend/executor/nodeSort.c index f8b5571a578..6f2e1f7f47b 100644 --- a/src/backend/executor/nodeSort.c +++ b/src/backend/executor/nodeSort.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSort.c,v 1.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 /* ---------------- diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 99b09f685a0..3d331c714f7 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.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) { diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 0978c1ec033..824ead5ec6b 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.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 @@ -462,14 +449,6 @@ ExecInitTidScan(TidScan *node, EState *estate, Plan *parent) 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. diff --git a/src/backend/executor/nodeUnique.c b/src/backend/executor/nodeUnique.c index add569a2c21..29c790b2c76 100644 --- a/src/backend/executor/nodeUnique.c +++ b/src/backend/executor/nodeUnique.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeUnique.c,v 1.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) |