diff --git a/src/backend/executor/execFlatten.c b/src/backend/executor/execFlatten.c index bb45e63a8a59d28a205cfab2cee08127c9667929..e94a43f3cf4f75067373f0f06a6b31bc4f36b05a 100644 --- a/src/backend/executor/execFlatten.c +++ b/src/backend/executor/execFlatten.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.12 2000/01/26 05:56:21 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/Attic/execFlatten.c,v 1.13 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -36,11 +36,12 @@ static bool FjoinBumpOuterNodes(TargetEntry *tlist, ExprContext *econtext, #endif + Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, - bool *resultIsNull, - bool *iterIsDone) + bool *isNull, + ExprDoneCond *isDone) { Node *expression; @@ -52,14 +53,14 @@ ExecEvalIter(Iter *iterNode, * only worrying about postquel functions, c functions will come * later. */ - return ExecEvalExpr(expression, econtext, resultIsNull, iterIsDone); + return ExecEvalExpr(expression, econtext, isNull, isDone); } void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, - bool *fj_isDone) + ExprDoneCond *fj_isDone) { #ifdef SETS_FIXED @@ -72,7 +73,7 @@ ExecEvalFjoin(TargetEntry *tlist, BoolPtr alwaysDone = fjNode->fj_alwaysDone; if (fj_isDone) - *fj_isDone = false; + *fj_isDone = ExprMultipleResult; /* * For the next tuple produced by the plan, we need to re-initialize diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 83117d836ebef83d7d5f9fb3233a40fb09c32708..3929c8782a9a6351ae5f520ff730f0db979832a3 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.78 2000/08/21 20:55:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.79 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -35,34 +35,34 @@ #include "postgres.h" #include "access/heapam.h" -#include "catalog/pg_language.h" #include "executor/execFlatten.h" #include "executor/execdebug.h" #include "executor/functions.h" #include "executor/nodeSubplan.h" #include "utils/array.h" #include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/fcache2.h" +#include "utils/fcache.h" /* static function decls */ -static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull); +static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, + bool *isNull); static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, - bool *isNull, bool *isDone); + bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull); static Datum ExecEvalOper(Expr *opClause, ExprContext *econtext, - bool *isNull); + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext, - bool *isNull, bool *isDone); -static void ExecEvalFuncArgs(FunctionCachePtr fcache, ExprContext *econtext, - List *argList, FunctionCallInfo fcinfo, - bool *argIsDone); + bool *isNull, ExprDoneCond *isDone); +static ExprDoneCond ExecEvalFuncArgs(FunctionCachePtr fcache, + List *argList, + ExprContext *econtext); static Datum ExecEvalNot(Expr *notclause, 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, - ExprContext *econtext, bool *isNull, bool *isDone); +static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); + /*---------- * ExecEvalArrayRef @@ -93,7 +93,7 @@ static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { ArrayType *array_source; ArrayType *resultArray; @@ -104,9 +104,6 @@ ExecEvalArrayRef(ArrayRef *arrayRef, IntArray upper, lower; int *lIndex; - bool dummy; - - *isNull = false; if (arrayRef->refexpr != NULL) { @@ -146,7 +143,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), econtext, isNull, - &dummy)); + NULL)); /* If any index expr yields NULL, result is NULL or source array */ if (*isNull) { @@ -168,7 +165,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), econtext, isNull, - &dummy)); + NULL)); /* If any index expr yields NULL, result is NULL or source array */ if (*isNull) { @@ -191,7 +188,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, Datum sourceData = ExecEvalExpr(arrayRef->refassgnexpr, econtext, isNull, - &dummy); + NULL); /* * For now, can't cope with inserting NULL into an array, * so make it a no-op per discussion above... @@ -588,162 +585,109 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) return (char *) retval; } - -static void +/* + * Evaluate arguments for a function. + */ +static ExprDoneCond ExecEvalFuncArgs(FunctionCachePtr fcache, - ExprContext *econtext, List *argList, - FunctionCallInfo fcinfo, - bool *argIsDone) + ExprContext *econtext) { + ExprDoneCond argIsDone; int i; List *arg; + argIsDone = ExprSingleResult; /* default assumption */ + i = 0; foreach(arg, argList) { + ExprDoneCond thisArgIsDone; - /* - * evaluate the expression, in general functions cannot take sets - * as arguments but we make an exception in the case of nested dot - * expressions. We have to watch out for this case here. - */ - fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg), - econtext, - &fcinfo->argnull[i], - argIsDone); + fcache->fcinfo.arg[i] = ExecEvalExpr((Node *) lfirst(arg), + econtext, + &fcache->fcinfo.argnull[i], + &thisArgIsDone); - if (!(*argIsDone)) + if (thisArgIsDone != ExprSingleResult) { - if (i != 0) - elog(ERROR, "functions can only take sets in their first argument"); - fcache->setArg = fcinfo->arg[0]; + /* + * We allow only one argument to have a set value; we'd need + * much more complexity to keep track of multiple set arguments + * (cf. ExecTargetList) and it doesn't seem worth it. + */ + if (argIsDone != ExprSingleResult) + elog(ERROR, "Functions and operators can take only one set argument"); fcache->hasSetArg = true; + argIsDone = thisArgIsDone; } i++; } + + return argIsDone; } /* * ExecMakeFunctionResult + * + * Evaluate the arguments to a function and then the function itself. + * + * NOTE: econtext is used only for evaluating the argument expressions; + * it is not passed to the function itself. */ -static Datum -ExecMakeFunctionResult(Node *node, +Datum +ExecMakeFunctionResult(FunctionCachePtr fcache, List *arguments, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { - FunctionCallInfoData fcinfo; - FunctionCachePtr fcache; - bool funcisset; - Datum result; - bool argDone; - - MemSet(&fcinfo, 0, sizeof(fcinfo)); - - /* - * This is kind of ugly, Func nodes now have targetlists so that we - * know when and what to project out from postquel function results. - * ExecMakeFunctionResult becomes a little bit more of a dual personality - * as a result. - */ - if (IsA(node, Func)) - { - fcache = ((Func *) node)->func_fcache; - funcisset = (((Func *) node)->funcid == F_SETEVAL); - } - else - { - fcache = ((Oper *) node)->op_fcache; - funcisset = false; - } - - fcinfo.flinfo = &fcache->func; - fcinfo.nargs = fcache->nargs; + Datum result; + ExprDoneCond argDone; + int i; /* * arguments is a list of expressions to evaluate before passing to - * the function manager. We collect the results of evaluating the - * expressions into the FunctionCallInfo struct. Note we assume that - * fcache->nargs is the correct length of the arguments list! + * the function manager. We skip the evaluation if it was already + * done in the previous call (ie, we are continuing the evaluation + * of a set-valued function). Otherwise, collect the current argument + * values into fcache->fcinfo. */ - if (fcache->nargs > 0) + if (fcache->fcinfo.nargs > 0 && !fcache->argsValid) { - if (fcache->nargs > FUNC_MAX_ARGS) - elog(ERROR, "ExecMakeFunctionResult: too many arguments"); - - /* - * If the setArg in the fcache is set we have an argument - * returning a set of tuples (i.e. a nested dot expression). We - * don't want to evaluate the arguments again until the function - * is done. hasSetArg will always be false until we eval the args - * for the first time. - */ - if (fcache->hasSetArg && fcache->setArg != (Datum) 0) - { - fcinfo.arg[0] = fcache->setArg; - argDone = false; - } - else - ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone); - - if (fcache->hasSetArg && argDone) + argDone = ExecEvalFuncArgs(fcache, arguments, econtext); + if (argDone == ExprEndResult) { - /* can only get here if input is an empty set. */ + /* input is an empty set, so return an empty set. */ *isNull = true; - *isDone = true; + if (isDone) + *isDone = ExprEndResult; + else + elog(ERROR, "Set-valued function called in context that cannot accept a set"); return (Datum) 0; } } - /* - * If this function is really a set, we have to diddle with things. If - * the function has already been called at least once, then the setArg - * field of the fcache holds the OID of this set in pg_proc. (This is - * not quite legit, since the setArg field is really for functions - * which take sets of tuples as input - set functions take no inputs - * at all. But it's a nice place to stash this value, for now.) - * - * If this is the first call of the set's function, then the call to - * ExecEvalFuncArgs above just returned the OID of the pg_proc tuple - * which defines this set. So replace the existing funcid in the - * funcnode with the set's OID. Also, we want a new fcache which - * points to the right function, so get that, now that we have the - * right OID. Also zero out fcinfo.arg, since the real set doesn't take - * any arguments. - */ - if (funcisset) - { - if (fcache->setArg) - { - ((Func *) node)->funcid = DatumGetObjectId(fcache->setArg); - } - else - { - ((Func *) node)->funcid = DatumGetObjectId(fcinfo.arg[0]); - setFcache(node, DatumGetObjectId(fcinfo.arg[0]), NIL, econtext); - fcache = ((Func *) node)->func_fcache; - fcache->setArg = fcinfo.arg[0]; - } - fcinfo.arg[0] = (Datum) 0; - } - /* * now return the value gotten by calling the function manager, * passing the function the evaluated parameter values. */ - if (fcache->language == SQLlanguageId) + if (fcache->func.fn_retset || fcache->hasSetArg) { - /*-------------------- - * This loop handles the situation where we are iterating through - * all results in a nested dot function (whose argument function - * returns a set of tuples) and the current function finally - * finishes. We need to get the next argument in the set and start - * the function all over again. We might have to do it more than - * once, if the function produces no results for a particular argument. - * This is getting unclean. - *-------------------- + /* + * We need to return a set result. Complain if caller not ready + * to accept one. + */ + if (isDone == NULL) + elog(ERROR, "Set-valued function called in context that cannot accept a set"); + + /* + * This loop handles the situation where we have both a set argument + * and a set-valued function. Once we have exhausted the function's + * value(s) for a particular argument value, we have to get the next + * argument value and start the function over again. We might have + * to do it more than once, if the function produces an empty result + * set for a particular input value. */ for (;;) { @@ -753,13 +697,11 @@ ExecMakeFunctionResult(Node *node, */ bool callit = true; - if (fcinfo.flinfo->fn_strict) + if (fcache->func.fn_strict) { - int i; - - for (i = 0; i < fcinfo.nargs; i++) + for (i = 0; i < fcache->fcinfo.nargs; i++) { - if (fcinfo.argnull[i]) + if (fcache->fcinfo.argnull[i]) { callit = false; break; @@ -769,35 +711,55 @@ ExecMakeFunctionResult(Node *node, if (callit) { - result = postquel_function(&fcinfo, fcache, isDone); - *isNull = fcinfo.isnull; + fcache->fcinfo.isnull = false; + fcache->rsinfo.isDone = ExprSingleResult; + result = FunctionCallInvoke(&fcache->fcinfo); + *isNull = fcache->fcinfo.isnull; + *isDone = fcache->rsinfo.isDone; } else { result = (Datum) 0; *isNull = true; - *isDone = true; + *isDone = ExprEndResult; + } + + if (*isDone != ExprEndResult) + { + /* + * Got a result from current argument. If function itself + * returns set, flag that we want to reuse current argument + * values on next call. + */ + if (fcache->func.fn_retset) + fcache->argsValid = true; + /* + * Make sure we say we are returning a set, even if the + * function itself doesn't return sets. + */ + *isDone = ExprMultipleResult; + break; } - if (!*isDone) - break; /* got a result from current argument */ + /* Else, done with this argument */ + fcache->argsValid = false; + if (!fcache->hasSetArg) break; /* input not a set, so done */ - /* OK, get the next argument... */ - ExecEvalFuncArgs(fcache, econtext, arguments, &fcinfo, &argDone); + /* Re-eval args to get the next element of the input set */ + argDone = ExecEvalFuncArgs(fcache, arguments, econtext); - if (argDone) + if (argDone != ExprMultipleResult) { /* - * End of arguments, so reset the setArg flag and say + * End of arguments, so reset the hasSetArg flag and say * "Done" */ - fcache->setArg = (Datum) 0; fcache->hasSetArg = false; - *isDone = true; *isNull = true; + *isDone = ExprEndResult; result = (Datum) 0; break; } @@ -807,50 +769,29 @@ ExecMakeFunctionResult(Node *node, * new argument. */ } - - if (funcisset) - { - - /* - * reset the funcid so that next call to this routine will - * still recognize this func as a set. Note that for now we - * assume that the set function in pg_proc must be a Postquel - * function - the funcid is not reset below for C functions. - */ - ((Func *) node)->funcid = F_SETEVAL; - - /* - * If we're done with the results of this function, get rid of - * its func cache. - */ - if (*isDone) - ((Func *) node)->func_fcache = NULL; - } } else { - /* A non-SQL function cannot return a set, at present. */ - *isDone = true; - /* + * Non-set case: much easier. + * * If function is strict, and there are any NULL arguments, * skip calling the function and return NULL. */ - if (fcinfo.flinfo->fn_strict) + if (fcache->func.fn_strict) { - int i; - - for (i = 0; i < fcinfo.nargs; i++) + for (i = 0; i < fcache->fcinfo.nargs; i++) { - if (fcinfo.argnull[i]) + if (fcache->fcinfo.argnull[i]) { *isNull = true; return (Datum) 0; } } } - result = FunctionCallInvoke(&fcinfo); - *isNull = fcinfo.isnull; + fcache->fcinfo.isnull = false; + result = FunctionCallInvoke(&fcache->fcinfo); + *isNull = fcache->fcinfo.isnull; } return result; @@ -871,12 +812,14 @@ ExecMakeFunctionResult(Node *node, * ---------------------------------------------------------------- */ static Datum -ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) +ExecEvalOper(Expr *opClause, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone) { Oper *op; List *argList; FunctionCachePtr fcache; - bool isDone; /* * we extract the oid of the function associated with the op and then @@ -894,16 +837,13 @@ ExecEvalOper(Expr *opClause, ExprContext *econtext, bool *isNull) fcache = op->op_fcache; if (fcache == NULL) { - setFcache((Node *) op, op->opid, argList, econtext); - fcache = op->op_fcache; + fcache = init_fcache(op->opid, length(argList), + econtext->ecxt_per_query_memory); + op->op_fcache = fcache; } - /* - * 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(fcache, argList, econtext, + isNull, isDone); } /* ---------------------------------------------------------------- @@ -915,7 +855,7 @@ static Datum ExecEvalFunc(Expr *funcClause, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { Func *func; List *argList; @@ -939,11 +879,12 @@ ExecEvalFunc(Expr *funcClause, fcache = func->func_fcache; if (fcache == NULL) { - setFcache((Node *) func, func->funcid, argList, econtext); - fcache = func->func_fcache; + fcache = init_fcache(func->funcid, length(argList), + econtext->ecxt_per_query_memory); + func->func_fcache = fcache; } - return ExecMakeFunctionResult((Node *) func, argList, econtext, + return ExecMakeFunctionResult(fcache, argList, econtext, isNull, isDone); } @@ -968,15 +909,10 @@ ExecEvalNot(Expr *notclause, ExprContext *econtext, bool *isNull) { Node *clause; Datum expr_value; - bool isDone; clause = lfirst(notclause->args); - /* - * We don't iterate over sets in the quals, so pass in an isDone flag, - * but ignore it. - */ - expr_value = ExecEvalExpr(clause, econtext, isNull, &isDone); + expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); /* * if the expression evaluates to null, then we just cascade the null @@ -1001,7 +937,6 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) { List *clauses; List *clause; - bool isDone; bool AnyNull; Datum clause_value; @@ -1024,15 +959,8 @@ ExecEvalOr(Expr *orExpr, ExprContext *econtext, bool *isNull) */ foreach(clause, clauses) { - - /* - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - */ clause_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); + econtext, isNull, NULL); /* * if we have a non-null true result, then return it. @@ -1057,7 +985,6 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) { List *clauses; List *clause; - bool isDone; bool AnyNull; Datum clause_value; @@ -1074,15 +1001,8 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) */ foreach(clause, clauses) { - - /* - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - */ clause_value = ExecEvalExpr((Node *) lfirst(clause), - econtext, - isNull, - &isDone); + econtext, isNull, NULL); /* * if we have a non-null false result, then return it. @@ -1108,12 +1028,12 @@ ExecEvalAnd(Expr *andExpr, ExprContext *econtext, bool *isNull) * ---------------------------------------------------------------- */ static Datum -ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) +ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone) { List *clauses; List *clause; Datum clause_value; - bool isDone; clauses = caseExpr->args; @@ -1126,14 +1046,10 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) { CaseWhen *wclause = lfirst(clause); - /* - * We don't iterate over sets in the quals, so pass in an isDone - * flag, but ignore it. - */ clause_value = ExecEvalExpr(wclause->expr, econtext, isNull, - &isDone); + NULL); /* * if we have a true test, then we return the result, since the @@ -1145,7 +1061,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) return ExecEvalExpr(wclause->result, econtext, isNull, - &isDone); + isDone); } } @@ -1154,7 +1070,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, bool *isNull) return ExecEvalExpr(caseExpr->defresult, econtext, isNull, - &isDone); + isDone); } *isNull = true; @@ -1171,7 +1087,7 @@ static Datum ExecEvalFieldSelect(FieldSelect *fselect, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { Datum result; TupleTableSlot *resSlot; @@ -1179,7 +1095,6 @@ ExecEvalFieldSelect(FieldSelect *fselect, result = ExecEvalExpr(fselect->arg, econtext, isNull, isDone); if (*isNull) return result; - /* XXX what about isDone? */ resSlot = (TupleTableSlot *) DatumGetPointer(result); Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot)); result = heap_getattr(resSlot->val, @@ -1194,28 +1109,52 @@ ExecEvalFieldSelect(FieldSelect *fselect, * * Recursively evaluate a targetlist or qualification expression. * - * 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(). + * Inputs: + * expression: the expression tree to evaluate + * econtext: evaluation context information + * + * Outputs: + * return value: Datum value of result + * *isNull: set to TRUE if result is NULL (actual return value is + * meaningless if so); set to FALSE if non-null result + * *isDone: set to indicator of set-result status + * + * A caller that can only accept a singleton (non-set) result should pass + * NULL for isDone; if the expression computes a set result then an elog() + * error will be reported. If the caller does pass an isDone pointer then + * *isDone is set to one of these three states: + * ExprSingleResult singleton result (not a set) + * ExprMultipleResult return value is one element of a set + * ExprEndResult there are no more elements in the set + * When ExprMultipleResult is returned, the caller should invoke + * ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult + * is returned after the last real set element. For convenience isNull will + * always be set TRUE when ExprEndResult is returned, but this should not be + * taken as indicating a NULL element of the set. Note that these return + * conventions allow us to distinguish among a singleton NULL, a NULL element + * of a set, and an empty set. * - * This routine is an inner loop routine and must 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(). + * + * This routine is an inner loop routine and must be as fast as possible. * ---------------------------------------------------------------- */ Datum ExecEvalExpr(Node *expression, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { Datum retDatum; /* Set default values for result flags: non-null, not a set result */ *isNull = false; - *isDone = true; + if (isDone) + *isDone = ExprSingleResult; /* Is this still necessary? Doubtful... */ if (expression == NULL) @@ -1266,7 +1205,8 @@ ExecEvalExpr(Node *expression, switch (expr->opType) { case OP_EXPR: - retDatum = ExecEvalOper(expr, econtext, isNull); + retDatum = ExecEvalOper(expr, econtext, + isNull, isDone); break; case FUNC_EXPR: retDatum = ExecEvalFunc(expr, econtext, @@ -1307,7 +1247,10 @@ ExecEvalExpr(Node *expression, isDone); break; case T_CaseExpr: - retDatum = ExecEvalCase((CaseExpr *) expression, econtext, isNull); + retDatum = ExecEvalCase((CaseExpr *) expression, + econtext, + isNull, + isDone); break; default: @@ -1328,7 +1271,7 @@ Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext, bool *isNull, - bool *isDone) + ExprDoneCond *isDone) { Datum retDatum; MemoryContext oldContext; @@ -1413,13 +1356,8 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) Node *clause = (Node *) lfirst(qlist); Datum expr_value; bool isNull; - bool isDone; - /* - * pass isDone, but ignore it. We don't iterate over multiple - * returns in the qualifications. - */ - expr_value = ExecEvalExpr(clause, econtext, &isNull, &isDone); + expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); if (isNull) { @@ -1496,6 +1434,11 @@ ExecCleanTargetListLength(List *targetlist) * * Evaluates a targetlist with respect to the current * expression context and return a tuple. + * + * As with ExecEvalExpr, the caller should pass isDone = NULL if not + * prepared to deal with sets of result tuples. Otherwise, a return + * of *isDone = ExprMultipleResult signifies a set element, and a return + * of *isDone = ExprEndResult signifies end of the set of tuple. * ---------------------------------------------------------------- */ static HeapTuple @@ -1504,24 +1447,22 @@ ExecTargetList(List *targetlist, TupleDesc targettype, Datum *values, ExprContext *econtext, - bool *isDone) + ExprDoneCond *isDone) { MemoryContext oldContext; - char nulls_array[64]; - bool fjNullArray[64]; - bool itemIsDoneArray[64]; - char *null_head; +#define NPREALLOCDOMAINS 64 + char nullsArray[NPREALLOCDOMAINS]; + bool fjIsNullArray[NPREALLOCDOMAINS]; + ExprDoneCond itemIsDoneArray[NPREALLOCDOMAINS]; + char *nulls; bool *fjIsNull; - bool *itemIsDone; + ExprDoneCond *itemIsDone; List *tl; TargetEntry *tle; - Node *expr; - Resdom *resdom; AttrNumber resind; - Datum constvalue; HeapTuple newTuple; bool isNull; - bool haveDoneIters; + bool haveDoneSets; static struct tupleDesc NullTupleDesc; /* we assume this inits to * zeroes */ @@ -1553,70 +1494,67 @@ ExecTargetList(List *targetlist, * we have a really large targetlist. otherwise we use the stack. * * We also allocate a bool array that is used to hold fjoin result state, - * and another that holds the isDone status for each targetlist item. + * and another array that holds the isDone status for each targetlist item. + * The isDone status is needed so that we can iterate, generating multiple + * tuples, when one or more tlist items return sets. (We expect the caller + * to call us again if we return *isDone = ExprMultipleResult.) */ - if (nodomains > 64) + if (nodomains > NPREALLOCDOMAINS) { - null_head = (char *) palloc(nodomains + 1); - fjIsNull = (bool *) palloc(nodomains + 1); - itemIsDone = (bool *) palloc(nodomains + 1); + nulls = (char *) palloc(nodomains * sizeof(char)); + fjIsNull = (bool *) palloc(nodomains * sizeof(bool)); + itemIsDone = (ExprDoneCond *) palloc(nodomains * sizeof(ExprDoneCond)); } else { - null_head = &nulls_array[0]; - fjIsNull = &fjNullArray[0]; - itemIsDone = &itemIsDoneArray[0]; + nulls = nullsArray; + fjIsNull = fjIsNullArray; + itemIsDone = itemIsDoneArray; } /* * evaluate all the expressions in the target list */ - *isDone = true; /* until proven otherwise */ - haveDoneIters = false; /* any isDone Iter exprs in tlist? */ + if (isDone) + *isDone = ExprSingleResult; /* until proven otherwise */ + + haveDoneSets = false; /* any exhausted set exprs in tlist? */ foreach(tl, targetlist) { - - /* - * remember, a target list is a list of lists: - * - * ((<resdom | fjoin> expr) (<resdom | fjoin> expr) ...) - * - * tl is a pointer to successive cdr's of the targetlist tle is a - * pointer to the target list entry in tl - */ tle = lfirst(tl); if (tle->resdom != NULL) { - expr = tle->expr; - resdom = tle->resdom; - resind = resdom->resno - 1; - - constvalue = ExecEvalExpr(expr, - econtext, - &isNull, - &itemIsDone[resind]); + resind = tle->resdom->resno - 1; - values[resind] = constvalue; + values[resind] = ExecEvalExpr(tle->expr, + econtext, + &isNull, + &itemIsDone[resind]); + nulls[resind] = isNull ? 'n' : ' '; - if (!isNull) - null_head[resind] = ' '; - else - null_head[resind] = 'n'; - - if (IsA(expr, Iter)) + if (itemIsDone[resind] != ExprSingleResult) { - if (itemIsDone[resind]) - haveDoneIters = true; + /* We have a set-valued expression in the tlist */ + if (isDone == NULL) + elog(ERROR, "Set-valued function called in context that cannot accept a set"); + if (itemIsDone[resind] == ExprMultipleResult) + { + /* we have undone sets in the tlist, set flag */ + *isDone = ExprMultipleResult; + } else - *isDone = false; /* we have undone Iters in the - * list */ + { + /* we have done sets in the tlist, set flag for that */ + haveDoneSets = true; + } } } else { +#ifdef SETS_FIXED int curNode; Resdom *fjRes; List *fjTlist = (List *) tle->expr; @@ -1626,9 +1564,12 @@ ExecTargetList(List *targetlist, ExecEvalFjoin(tle, econtext, fjIsNull, isDone); - /* this is probably wrong: */ - if (*isDone) + /* XXX this is wrong, but since fjoin code is completely broken + * anyway, I'm not going to worry about it now --- tgl 8/23/00 + */ + if (isDone && *isDone == ExprEndResult) { + MemoryContextSwitchTo(oldContext); newTuple = NULL; goto exit; } @@ -1638,13 +1579,8 @@ ExecTargetList(List *targetlist, */ fjRes = (Resdom *) fjNode->fj_innerNode; resind = fjRes->resno - 1; - if (fjIsNull[0]) - null_head[resind] = 'n'; - else - { - null_head[resind] = ' '; - values[resind] = results[0]; - } + values[resind] = results[0]; + nulls[resind] = fjIsNull[0] ? 'n' : ' '; /* * Get results from all of the outer nodes @@ -1653,32 +1589,32 @@ ExecTargetList(List *targetlist, curNode < nNodes; curNode++, fjTlist = lnext(fjTlist)) { -#ifdef NOT_USED /* what is this?? */ Node *outernode = lfirst(fjTlist); fjRes = (Resdom *) outernode->iterexpr; -#endif resind = fjRes->resno - 1; - if (fjIsNull[curNode]) - null_head[resind] = 'n'; - else - { - null_head[resind] = ' '; - values[resind] = results[curNode]; - } + values[resind] = results[curNode]; + nulls[resind] = fjIsNull[curNode] ? 'n' : ' '; } +#else + elog(ERROR, "ExecTargetList: fjoin nodes not currently supported"); +#endif } } - if (haveDoneIters) + if (haveDoneSets) { - if (*isDone) + /* + * note: can't get here unless we verified isDone != NULL + */ + if (*isDone == ExprSingleResult) { /* - * all Iters are done, so return a null indicating tlist set - * expansion is complete. + * all sets are done, so report that tlist expansion is complete. */ + *isDone = ExprEndResult; + MemoryContextSwitchTo(oldContext); newTuple = NULL; goto exit; } @@ -1686,22 +1622,8 @@ ExecTargetList(List *targetlist, { /* - * We have some done and some undone Iters. Restart the done + * We have some done and some undone sets. Restart the done * ones so that we can deliver a tuple (if possible). - * - * XXX this code is a crock, because it only works for Iters at - * the top level of tlist expressions, and doesn't even work - * right for them: you should get all possible combinations of - * Iter results, but you won't unless the numbers of values - * returned by each are relatively prime. Should have a - * mechanism more like aggregate functions, where we make a - * list of all Iters contained in the tlist and cycle through - * their values in a methodical fashion. To do someday; can't - * get excited about fixing a Berkeley feature that's not in - * SQL92. (The only reason we're doing this much is that we - * have to be sure all the Iters are run to completion, or - * their subplan executors will have unreleased resources, - * e.g. pinned buffers...) */ foreach(tl, targetlist) { @@ -1709,36 +1631,57 @@ ExecTargetList(List *targetlist, if (tle->resdom != NULL) { - expr = tle->expr; - resdom = tle->resdom; - resind = resdom->resno - 1; + resind = tle->resdom->resno - 1; - if (IsA(expr, Iter) &&itemIsDone[resind]) + if (itemIsDone[resind] == ExprEndResult) { - constvalue = ExecEvalExpr(expr, - econtext, - &isNull, - &itemIsDone[resind]); - if (itemIsDone[resind]) + values[resind] = ExecEvalExpr(tle->expr, + econtext, + &isNull, + &itemIsDone[resind]); + nulls[resind] = isNull ? 'n' : ' '; + + if (itemIsDone[resind] == ExprEndResult) { /* - * Oh dear, this Iter is returning an empty + * Oh dear, this item is returning an empty * set. Guess we can't make a tuple after all. */ - *isDone = true; - newTuple = NULL; - goto exit; + *isDone = ExprEndResult; + break; } + } + } + } + /* + * If we cannot make a tuple because some sets are empty, + * we still have to cycle the nonempty sets to completion, + * else resources will not be released from subplans etc. + */ + if (*isDone == ExprEndResult) + { + foreach(tl, targetlist) + { + tle = lfirst(tl); - values[resind] = constvalue; + if (tle->resdom != NULL) + { + resind = tle->resdom->resno - 1; - if (!isNull) - null_head[resind] = ' '; - else - null_head[resind] = 'n'; + while (itemIsDone[resind] == ExprMultipleResult) + { + (void) ExecEvalExpr(tle->expr, + econtext, + &isNull, + &itemIsDone[resind]); + } } } + + MemoryContextSwitchTo(oldContext); + newTuple = NULL; + goto exit; } } } @@ -1748,30 +1691,27 @@ ExecTargetList(List *targetlist, */ MemoryContextSwitchTo(oldContext); - newTuple = (HeapTuple) heap_formtuple(targettype, values, null_head); + newTuple = (HeapTuple) heap_formtuple(targettype, values, nulls); exit: /* * free the status arrays if we palloc'd them */ - if (nodomains > 64) + if (nodomains > NPREALLOCDOMAINS) { - pfree(null_head); + pfree(nulls); pfree(fjIsNull); pfree(itemIsDone); } - /* make sure we are in the right context if we did "goto exit" */ - MemoryContextSwitchTo(oldContext); - return newTuple; } /* ---------------------------------------------------------------- * ExecProject * - * projects a tuple based in projection info and stores + * projects a tuple based on projection info and stores * it in the specified tuple table slot. * * Note: someday soon the executor can be extended to eliminate @@ -1782,7 +1722,7 @@ exit: * ---------------------------------------------------------------- */ TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, bool *isDone) +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) { TupleTableSlot *slot; List *targetlist; @@ -1810,7 +1750,7 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone) econtext = projInfo->pi_exprContext; /* - * form a new (result) tuple + * form a new result tuple (if possible --- result can be NULL) */ newTuple = ExecTargetList(targetlist, len, @@ -1822,9 +1762,8 @@ ExecProject(ProjectionInfo *projInfo, bool *isDone) /* * store the tuple in the projection slot and return the slot. */ - return (TupleTableSlot *) - ExecStoreTuple(newTuple,/* tuple to store */ - slot, /* slot to store in */ - InvalidBuffer, /* tuple has no buffer */ - true); + return ExecStoreTuple(newTuple, /* tuple to store */ + slot, /* slot to store in */ + InvalidBuffer, /* tuple has no buffer */ + true); } diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index a3f66d20cad24d39cfdf3921c742ce4d26a00279..d000a4cf50ad0f446a014604e4dd541750d04df0 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.13 2000/07/17 03:04:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execScan.c,v 1.14 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,11 +50,10 @@ ExecScan(Scan *node, { CommonScanState *scanstate; EState *estate; + ExprContext *econtext; List *qual; - bool isDone; + ExprDoneCond isDone; TupleTableSlot *resultSlot; - ExprContext *econtext; - ProjectionInfo *projInfo; /* ---------------- * Fetch data from node @@ -65,13 +64,6 @@ ExecScan(Scan *node, econtext = scanstate->cstate.cs_ExprContext; qual = node->plan.qual; - /* ---------------- - * 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 * scan tuple (because there is a function-returning-set in the @@ -80,14 +72,21 @@ ExecScan(Scan *node, */ if (scanstate->cstate.cs_TupFromTlist) { - projInfo = scanstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - if (!isDone) + resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone); + if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ scanstate->cstate.cs_TupFromTlist = false; } + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. Note this can't + * happen until we're done projecting out tuples from a scan tuple. + * ---------------- + */ + ResetExprContext(econtext); + /* * get a tuple from the access method loop until we obtain a tuple * which passes the qualification. @@ -121,8 +120,6 @@ ExecScan(Scan *node, /* ---------------- * check that the current tuple satisfies the qual-clause - * if our qualification succeeds then we may - * leave the loop. * * check for non-nil qual here to avoid a function call to * ExecQual() when the qual is nil ... saves only a few cycles, @@ -130,7 +127,22 @@ ExecScan(Scan *node, * ---------------- */ if (!qual || ExecQual(qual, econtext, false)) - break; + { + /* ---------------- + * Found a satisfactory scan tuple. + * + * Form a projection tuple, store it in the result tuple + * slot and return it --- unless we find we can project no + * tuples from this scan tuple, in which case continue scan. + * ---------------- + */ + resultSlot = ExecProject(scanstate->cstate.cs_ProjInfo, &isDone); + if (isDone != ExprEndResult) + { + scanstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult); + return resultSlot; + } + } /* ---------------- * Tuple fails qual, so free per-tuple memory and try again. @@ -138,18 +150,4 @@ ExecScan(Scan *node, */ ResetExprContext(econtext); } - - /* ---------------- - * Found a satisfactory scan tuple. - * - * Form a projection tuple, store it in the result tuple - * slot and return it. - * ---------------- - */ - projInfo = scanstate->cstate.cs_ProjInfo; - - resultSlot = ExecProject(projInfo, &isDone); - scanstate->cstate.cs_TupFromTlist = !isDone; - - return resultSlot; } diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 001feb267ffe02e0b65f14228ae3f8dc902f0430..58fb68a6113b241f9a5867661039817125c5a680 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,22 +8,29 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.37 2000/08/08 15:41:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.38 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" #include "access/heapam.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" #include "executor/execdefs.h" #include "executor/executor.h" #include "executor/functions.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" +#include "utils/builtins.h" #include "utils/datum.h" +#include "utils/syscache.h" +/* + * We have an execution_state record for each query in the function. + */ typedef enum { F_EXEC_START, F_EXEC_RUN, F_EXEC_DONE @@ -39,15 +46,40 @@ typedef struct local_es #define LAST_POSTQUEL_COMMAND(es) ((es)->next == (execution_state *) NULL) + +/* + * An SQLFunctionCache record is built during the first call, + * and linked to from the fn_extra field of the FmgrInfo struct. + */ + +typedef struct +{ + int typlen; /* length of the return type */ + bool typbyval; /* true if return type is pass by value */ + bool returnsTuple; /* true if return type is a tuple */ + + TupleTableSlot *funcSlot; /* if one result we need to copy it before + * we end execution of the function and + * free stuff */ + + /* head of linked list of execution_state records */ + execution_state *func_state; +} SQLFunctionCache; + +typedef SQLFunctionCache *SQLFunctionCachePtr; + + /* non-export function prototypes */ +static execution_state *init_execution_state(char *src, + Oid *argOidVect, int nargs); +static void init_sql_fcache(FmgrInfo *finfo); static TupleDesc postquel_start(execution_state *es); -static execution_state *init_execution_state(FunctionCachePtr fcache); static TupleTableSlot *postquel_getnext(execution_state *es); static void postquel_end(execution_state *es); static void postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo); static Datum postquel_execute(execution_state *es, FunctionCallInfo fcinfo, - FunctionCachePtr fcache); + SQLFunctionCachePtr fcache); static Datum @@ -69,21 +101,19 @@ ProjectAttribute(HeapTuple tup, } static execution_state * -init_execution_state(FunctionCachePtr fcache) +init_execution_state(char *src, Oid *argOidVect, int nargs) { execution_state *newes; execution_state *nextes; execution_state *preves; List *queryTree_list, *qtl_item; - int nargs = fcache->nargs; newes = (execution_state *) palloc(sizeof(execution_state)); nextes = newes; preves = (execution_state *) NULL; - queryTree_list = pg_parse_and_rewrite(fcache->src, - fcache->argOidVect, nargs); + queryTree_list = pg_parse_and_rewrite(src, argOidVect, nargs); foreach(qtl_item, queryTree_list) { @@ -138,6 +168,134 @@ init_execution_state(FunctionCachePtr fcache) return newes; } + +static void +init_sql_fcache(FmgrInfo *finfo) +{ + Oid foid = finfo->fn_oid; + HeapTuple procedureTuple; + HeapTuple typeTuple; + Form_pg_proc procedureStruct; + Form_pg_type typeStruct; + SQLFunctionCachePtr fcache; + Oid *argOidVect; + char *src; + int nargs; + Datum tmp; + bool isNull; + + /* ---------------- + * get the procedure tuple corresponding to the given function Oid + * + * NB: use SearchSysCacheTupleCopy to ensure tuple lives long enough + * ---------------- + */ + procedureTuple = SearchSysCacheTupleCopy(PROCOID, + ObjectIdGetDatum(foid), + 0, 0, 0); + + if (!HeapTupleIsValid(procedureTuple)) + elog(ERROR, "init_sql_fcache: Cache lookup failed for procedure %u", + foid); + + procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + + /* ---------------- + * get the return type from the procedure tuple + * ---------------- + */ + typeTuple = SearchSysCacheTuple(TYPEOID, + ObjectIdGetDatum(procedureStruct->prorettype), + 0, 0, 0); + + if (!HeapTupleIsValid(typeTuple)) + elog(ERROR, "init_sql_fcache: Cache lookup failed for type %u", + procedureStruct->prorettype); + + typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); + + fcache = (SQLFunctionCachePtr) palloc(sizeof(SQLFunctionCache)); + MemSet(fcache, 0, sizeof(SQLFunctionCache)); + + /* ---------------- + * get the type length and by-value flag from the type tuple + * ---------------- + */ + fcache->typlen = typeStruct->typlen; + if (typeStruct->typrelid == InvalidOid) + { + /* The return type is not a relation, so just use byval */ + fcache->typbyval = typeStruct->typbyval; + fcache->returnsTuple = false; + } + else + { + + /* + * This is a hack. We assume here that any function returning a + * tuple returns it by reference. This needs to be fixed, since + * actually the mechanism isn't quite like return-by-reference. + */ + fcache->typbyval = false; + fcache->returnsTuple = true; + } + + /* + * If we are returning exactly one result then we have to copy tuples + * and by reference results because we have to end the execution + * before we return the results. When you do this everything + * allocated by the executor (i.e. slots and tuples) is freed. + */ + if (!finfo->fn_retset && !fcache->typbyval) + { + TupleTableSlot *slot; + + slot = makeNode(TupleTableSlot); + slot->val = (HeapTuple) NULL; + slot->ttc_shouldFree = true; + slot->ttc_descIsNew = true; + slot->ttc_tupleDescriptor = (TupleDesc) NULL; + slot->ttc_buffer = InvalidBuffer; + slot->ttc_whichplan = -1; + + fcache->funcSlot = slot; + } + else + fcache->funcSlot = NULL; + + nargs = procedureStruct->pronargs; + + if (nargs > 0) + { + argOidVect = (Oid *) palloc(nargs * sizeof(Oid)); + memcpy(argOidVect, + procedureStruct->proargtypes, + nargs * sizeof(Oid)); + } + else + { + argOidVect = (Oid *) NULL; + } + + tmp = SysCacheGetAttr(PROCOID, + procedureTuple, + Anum_pg_proc_prosrc, + &isNull); + if (isNull) + elog(ERROR, "init_sql_fcache: null prosrc for procedure %u", + foid); + src = DatumGetCString(DirectFunctionCall1(textout, tmp)); + + fcache->func_state = init_execution_state(src, argOidVect, nargs); + + pfree(src); + + heap_freetuple(procedureTuple); + + finfo->fn_extra = (void *) fcache; +} + + static TupleDesc postquel_start(execution_state *es) { @@ -208,7 +366,7 @@ postquel_sub_params(execution_state *es, FunctionCallInfo fcinfo) } static TupleTableSlot * -copy_function_result(FunctionCachePtr fcache, +copy_function_result(SQLFunctionCachePtr fcache, TupleTableSlot *resultSlot) { TupleTableSlot *funcSlot; @@ -219,10 +377,10 @@ copy_function_result(FunctionCachePtr fcache, Assert(!TupIsNull(resultSlot)); resultTuple = resultSlot->val; - funcSlot = (TupleTableSlot *) fcache->funcSlot; + funcSlot = fcache->funcSlot; - if (funcSlot == (TupleTableSlot *) NULL) - return resultSlot; + if (funcSlot == NULL) + return resultSlot; /* no need to copy result */ /* * If first time through, we have to initialize the funcSlot's @@ -243,7 +401,7 @@ copy_function_result(FunctionCachePtr fcache, static Datum postquel_execute(execution_state *es, FunctionCallInfo fcinfo, - FunctionCachePtr fcache) + SQLFunctionCachePtr fcache) { TupleTableSlot *slot; Datum value; @@ -319,7 +477,7 @@ postquel_execute(execution_state *es, * If this is a single valued function we have to end the function * execution now. */ - if (!fcache->returnsSet) + if (!fcinfo->flinfo->fn_retset) { postquel_end(es); es->status = F_EXEC_DONE; @@ -338,11 +496,10 @@ postquel_execute(execution_state *es, } Datum -postquel_function(FunctionCallInfo fcinfo, - FunctionCachePtr fcache, - bool *isDone) +fmgr_sql(PG_FUNCTION_ARGS) { MemoryContext oldcontext; + SQLFunctionCachePtr fcache; execution_state *es; Datum result = 0; CommandId savedId; @@ -352,7 +509,7 @@ postquel_function(FunctionCallInfo fcinfo, * parsetrees, plans, etc, will have sufficient lifetime. The * sub-executor is responsible for deleting per-tuple information. */ - oldcontext = MemoryContextSwitchTo(fcache->fcacheCxt); + oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); /* * Before we start do anything we must save CurrentScanCommandId to @@ -362,13 +519,21 @@ postquel_function(FunctionCallInfo fcinfo, savedId = GetScanCommandId(); SetScanCommandId(GetCurrentCommandId()); - es = (execution_state *) fcache->func_state; - if (es == NULL) + /* + * Initialize fcache and execution state if first time through. + */ + fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; + if (fcache == NULL) { - es = init_execution_state(fcache); - fcache->func_state = (char *) es; + init_sql_fcache(fcinfo->flinfo); + fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; } + es = fcache->func_state; + Assert(es); + /* + * Find first unfinished query in function. + */ while (es && es->status == F_EXEC_DONE) es = es->next; @@ -401,7 +566,7 @@ postquel_function(FunctionCallInfo fcinfo, /* * Reset the execution states to start over again */ - es = (execution_state *) fcache->func_state; + es = fcache->func_state; while (es) { es->status = F_EXEC_START; @@ -411,9 +576,21 @@ postquel_function(FunctionCallInfo fcinfo, /* * Let caller know we're finished. */ - *isDone = true; + if (fcinfo->flinfo->fn_retset) + { + ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; + + if (rsi && IsA(rsi, ReturnSetInfo)) + rsi->isDone = ExprEndResult; + else + elog(ERROR, "Set-valued function called in context that cannot accept a set"); + fcinfo->isnull = true; + result = (Datum) 0; + } + MemoryContextSwitchTo(oldcontext); - return (fcache->returnsSet) ? (Datum) NULL : result; + + return result; } /* @@ -422,7 +599,18 @@ postquel_function(FunctionCallInfo fcinfo, */ Assert(LAST_POSTQUEL_COMMAND(es)); - *isDone = false; + /* + * Let caller know we're not finished. + */ + if (fcinfo->flinfo->fn_retset) + { + ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; + + if (rsi && IsA(rsi, ReturnSetInfo)) + rsi->isDone = ExprMultipleResult; + else + elog(ERROR, "Set-valued function called in context that cannot accept a set"); + } MemoryContextSwitchTo(oldcontext); diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 547a946b4ce81bf941dfb5ae41181d8dd591b0c6..fae1a5b0d87feeef87bef2e7937b1028e8ca85db 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -34,7 +34,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.70 2000/07/17 03:04:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeAgg.c,v 1.71 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -451,7 +451,6 @@ ExecAgg(Agg *node) TupleTableSlot *resultSlot; HeapTuple inputTuple; int aggno; - bool isDone; bool isNull; /* --------------------- @@ -523,7 +522,7 @@ ExecAgg(Agg *node) Datum newVal; newVal = ExecEvalExpr(aggref->target, econtext, - &isNull, &isDone); + &isNull, NULL); if (aggref->aggdistinct) { @@ -677,8 +676,9 @@ ExecAgg(Agg *node) /* * Form a projection tuple using the aggregate results and the * representative input tuple. Store it in the result tuple slot. + * Note we do not support aggregates returning sets ... */ - resultSlot = ExecProject(projInfo, &isDone); + resultSlot = ExecProject(projInfo, NULL); /* * If the completed tuple does not match the qualifications, it is diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index 8a445b53d4197944d0c7bea3eaec850147c79136..8fc319a77f0e67c770b0c5e12e9de473ca230df3 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.37 2000/07/12 02:37:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeGroup.c,v 1.38 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -73,7 +73,6 @@ ExecGroupEveryTuple(Group *node) TupleTableSlot *outerslot; ProjectionInfo *projInfo; TupleTableSlot *resultSlot; - bool isDone; /* --------------------- * get state info from node @@ -163,7 +162,7 @@ ExecGroupEveryTuple(Group *node) projInfo = grpstate->csstate.cstate.cs_ProjInfo; econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); + resultSlot = ExecProject(projInfo, NULL); return resultSlot; } @@ -185,7 +184,6 @@ ExecGroupOneTuple(Group *node) TupleTableSlot *outerslot; ProjectionInfo *projInfo; TupleTableSlot *resultSlot; - bool isDone; /* --------------------- * get state info from node @@ -258,7 +256,7 @@ ExecGroupOneTuple(Group *node) grpstate->csstate.css_ScanTupleSlot, InvalidBuffer, false); econtext->ecxt_scantuple = grpstate->csstate.css_ScanTupleSlot; - resultSlot = ExecProject(projInfo, &isDone); + resultSlot = ExecProject(projInfo, NULL); /* save outerTuple if we are not done yet */ if (!grpstate->grp_done) diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 682afdba4afa7e9620034af6501897a868175b44..9cd85195cdcc4fe85ea3de4328a3c0b95b1ca168 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.51 2000/08/22 04:06:19 tgl Exp $ + * $Id: nodeHash.c,v 1.52 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -524,7 +524,6 @@ ExecHashGetBucket(HashJoinTable hashtable, int bucketno; Datum keyval; bool isNull; - bool isDone; /* ---------------- * Get the join attribute value of the tuple @@ -535,8 +534,7 @@ ExecHashGetBucket(HashJoinTable hashtable, */ ResetExprContext(econtext); - keyval = ExecEvalExprSwitchContext(hashkey, econtext, - &isNull, &isDone); + keyval = ExecEvalExprSwitchContext(hashkey, econtext, &isNull, NULL); /* ------------------ * compute the hash function diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 54af882db12333b3d914b5091deab068467d99ec..4b3b4a825050e4acdaa5a17c1f3c93030a2ec81f 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.32 2000/07/17 03:04:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.33 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,6 +55,7 @@ ExecHashJoin(HashJoin *node) TupleTableSlot *inntuple; Node *outerVar; ExprContext *econtext; + ExprDoneCond isDone; HashJoinTable hashtable; HeapTuple curtuple; TupleTableSlot *outerTupleSlot; @@ -83,13 +84,6 @@ ExecHashJoin(HashJoin *node) hashtable = hjstate->hj_HashTable; econtext = hjstate->jstate.cs_ExprContext; - /* ---------------- - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. - * ---------------- - */ - ResetExprContext(econtext); - /* ---------------- * Check to see if we're still projecting out tuples from a previous * join tuple (because there is a function-returning-set in the @@ -99,15 +93,22 @@ ExecHashJoin(HashJoin *node) if (hjstate->jstate.cs_TupFromTlist) { TupleTableSlot *result; - bool isDone; result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) + if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ hjstate->jstate.cs_TupFromTlist = false; } + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. Note this can't + * happen until we're done projecting out tuples from a join tuple. + * ---------------- + */ + ResetExprContext(econtext); + /* ---------------- * if this is the first call, build the hash table for inner relation * ---------------- @@ -241,15 +242,15 @@ ExecHashJoin(HashJoin *node) */ if (ExecQual(qual, econtext, false)) { - ProjectionInfo *projInfo; TupleTableSlot *result; - bool isDone; hjstate->jstate.cs_OuterTupleSlot = outerTupleSlot; - projInfo = hjstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - hjstate->jstate.cs_TupFromTlist = !isDone; - return result; + result = ExecProject(hjstate->jstate.cs_ProjInfo, &isDone); + if (isDone != ExprEndResult) + { + hjstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult); + return result; + } } } diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 57770d2405806ce5c06fadfc2bce8a95ccbce3bb..a8b29514b882e9538792f0d56f4488f816215413 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.53 2000/08/13 02:50:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeIndexscan.c,v 1.54 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -334,7 +334,6 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) Node *scanexpr; Datum scanvalue; bool isNull; - bool isDone; estate = node->scan.plan.state; indexstate = node->indxstate; @@ -411,14 +410,10 @@ ExecIndexReScan(IndexScan *node, ExprContext *exprCtxt, Plan *parent) (Node *) get_rightop(clause) : (Node *) get_leftop(clause); - /* - * pass in isDone but ignore it. We don't iterate in - * quals - */ scanvalue = ExecEvalExprSwitchContext(scanexpr, econtext, &isNull, - &isDone); + NULL); scan_keys[j].sk_argument = scanvalue; if (isNull) scan_keys[j].sk_flags |= SK_ISNULL; diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index a3f92b06901392dec85ab99cb15335a3c09cb394..5a2f45028a0348d44ed9e75352eb472e9e96a44d 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.36 2000/07/12 02:37:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.37 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -226,18 +226,16 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) { Datum const_value; bool isNull; - bool isDone; /* ---------------- * first test if our compare clause is satisfied. * if so then return true. * * A NULL result is considered false. - * ignore isDone, don't iterate in quals. * ---------------- */ const_value = ExecEvalExpr((Node *) lfirst(clause), econtext, - &isNull, &isDone); + &isNull, NULL); if (DatumGetBool(const_value) && !isNull) { @@ -254,7 +252,7 @@ MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext) const_value = ExecEvalExpr((Node *) lfirst(eqclause), econtext, &isNull, - &isDone); + NULL); if (! DatumGetBool(const_value) || isNull) break; /* return false */ @@ -447,13 +445,6 @@ ExecMergeJoin(MergeJoin *node) innerSkipQual = mergestate->mj_OuterSkipQual; } - /* ---------------- - * 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 @@ -463,15 +454,23 @@ ExecMergeJoin(MergeJoin *node) if (mergestate->jstate.cs_TupFromTlist) { TupleTableSlot *result; - bool isDone; + ExprDoneCond isDone; result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone); - if (!isDone) + if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ mergestate->jstate.cs_TupFromTlist = false; } + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. Note this can't + * happen until we're done projecting out tuples from a join tuple. + * ---------------- + */ + ResetExprContext(econtext); + /* ---------------- * ok, everything is setup.. let's go to work * ---------------- @@ -599,17 +598,19 @@ ExecMergeJoin(MergeJoin *node) * projection tuple and return the slot containing it. * ---------------- */ - ProjectionInfo *projInfo; TupleTableSlot *result; - bool isDone; + ExprDoneCond isDone; MJ_printf("ExecMergeJoin: **** returning tuple ****\n"); - projInfo = mergestate->jstate.cs_ProjInfo; + result = ExecProject(mergestate->jstate.cs_ProjInfo, + &isDone); - result = ExecProject(projInfo, &isDone); - mergestate->jstate.cs_TupFromTlist = !isDone; - return result; + if (isDone != ExprEndResult) + { + mergestate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult); + return result; + } } break; diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index f59c1b0f602d2d518abb15581308da85752a7946..3685232c7e42b15e5b9777e81a573920d461ec5f 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.19 2000/08/13 02:50:03 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.20 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -87,13 +87,6 @@ ExecNestLoop(NestLoop *node) outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; - /* ---------------- - * 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 @@ -103,15 +96,23 @@ ExecNestLoop(NestLoop *node) if (nlstate->jstate.cs_TupFromTlist) { TupleTableSlot *result; - bool isDone; + ExprDoneCond isDone; result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); - if (!isDone) + if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ nlstate->jstate.cs_TupFromTlist = false; } + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. Note this can't + * happen until we're done projecting out tuples from a join tuple. + * ---------------- + */ + ResetExprContext(econtext); + /* ---------------- * Ok, everything is setup for the join so now loop until * we return a qualifying join tuple.. @@ -219,16 +220,18 @@ ExecNestLoop(NestLoop *node) * using ExecProject(). * ---------------- */ - ProjectionInfo *projInfo; TupleTableSlot *result; - bool isDone; + ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); - projInfo = nlstate->jstate.cs_ProjInfo; - result = ExecProject(projInfo, &isDone); - nlstate->jstate.cs_TupFromTlist = !isDone; - return result; + result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); + + if (isDone != ExprEndResult) + { + nlstate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult); + return result; + } } /* ---------------- diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 770cc47ccc4f405b80e6f1083e1b35ac6c1c36f7..e36037de7108f63004728135099024581b025fc2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -34,7 +34,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.15 2000/07/17 03:04:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeResult.c,v 1.16 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -67,8 +67,7 @@ ExecResult(Result *node) TupleTableSlot *resultSlot; Plan *outerPlan; ExprContext *econtext; - bool isDone; - ProjectionInfo *projInfo; + ExprDoneCond isDone; /* ---------------- * initialize the result node's state @@ -77,13 +76,6 @@ ExecResult(Result *node) resstate = node->resstate; econtext = resstate->cstate.cs_ExprContext; - /* ---------------- - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. - * ---------------- - */ - ResetExprContext(econtext); - /* ---------------- * check constant qualifications like (2 > 1), if not already done * ---------------- @@ -111,12 +103,20 @@ ExecResult(Result *node) if (resstate->cstate.cs_TupFromTlist) { resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone); - if (!isDone) + if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ resstate->cstate.cs_TupFromTlist = false; } + /* ---------------- + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. Note this can't + * happen until we're done projecting out tuples from a scan tuple. + * ---------------- + */ + ResetExprContext(econtext); + /* ---------------- * 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() @@ -124,7 +124,7 @@ ExecResult(Result *node) * Either way, now we are through. * ---------------- */ - if (!resstate->rs_done) + while (!resstate->rs_done) { outerPlan = outerPlan(node); @@ -159,13 +159,18 @@ ExecResult(Result *node) } /* ---------------- - * form the result tuple using ExecProject(), and return it. + * form the result tuple using ExecProject(), and return it + * --- unless the projection produces an empty set, in which case + * we must loop back to see if there are more outerPlan tuples. * ---------------- */ - projInfo = resstate->cstate.cs_ProjInfo; - resultSlot = ExecProject(projInfo, &isDone); - resstate->cstate.cs_TupFromTlist = !isDone; - return resultSlot; + resultSlot = ExecProject(resstate->cstate.cs_ProjInfo, &isDone); + + if (isDone != ExprEndResult) + { + resstate->cstate.cs_TupFromTlist = (isDone == ExprMultipleResult); + return resultSlot; + } } return NULL; diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index 3d331c714f74ef853d3150052499a663ba135bf1..aee6911e5e424d5794d624db3473f9373c393e25 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.26 2000/07/12 02:37:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeSubplan.c,v 1.27 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -40,7 +40,6 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) MemoryContext oldcontext; TupleTableSlot *slot; Datum result; - bool isDone; bool found = false; /* TRUE if got at least one subplan tuple */ List *lst; @@ -67,9 +66,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) prm->value = ExecEvalExprSwitchContext((Node *) lfirst(pvar), econtext, &(prm->isnull), - &isDone); - if (!isDone) - elog(ERROR, "ExecSubPlan: set values not supported for params"); + NULL); pvar = lnext(pvar); } plan->chgParam = nconc(plan->chgParam, listCopy(node->parParam)); @@ -189,9 +186,7 @@ ExecSubPlan(SubPlan *node, List *pvar, ExprContext *econtext, bool *isNull) * Now we can eval the combining operator for this column. */ expresult = ExecEvalExprSwitchContext((Node *) expr, econtext, - &expnull, &isDone); - if (!isDone) - elog(ERROR, "ExecSubPlan: set values not supported for combining operators"); + &expnull, NULL); /* * Combine the result into the row result as appropriate. diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index ac6511dcbfb5a0a4688bbc51a06738c7fa5834fe..055d07998b9108837c99ec3543d9b562fbb601a3 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.11 2000/08/03 19:19:30 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/nodeTidscan.c,v 1.12 2000/08/24 03:29:03 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,7 +38,6 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList) List *lst; ItemPointer itemptr; bool isNull; - bool isDone; int numTids = 0; foreach(lst, evalList) @@ -47,8 +46,8 @@ TidListCreate(List *evalList, ExprContext *econtext, ItemPointer *tidList) DatumGetPointer(ExecEvalExprSwitchContext(lfirst(lst), econtext, &isNull, - &isDone)); - if (itemptr && ItemPointerIsValid(itemptr)) + NULL)); + if (!isNull && itemptr && ItemPointerIsValid(itemptr)) { tidList[numTids] = itemptr; numTids++; diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 27ae4056d99124a82d478d4e0b4fa08c48d686b5..05f32d25972d24dd10a4f72dba920807d0b9c704 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.93 2000/08/13 02:50:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.94 2000/08/24 03:29:04 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1380,9 +1380,8 @@ clause_pred_clause_test(Expr *predicate, Node *clause) copyObject(clause_const), copyObject(pred_const)); -#ifndef OMIT_PARTIAL_INDEX test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL); -#endif /* OMIT_PARTIAL_INDEX */ + if (isNull) { elog(DEBUG, "clause_pred_clause_test: null test result"); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 22d93b26e7ba33c0d62f378565f34f1852b66582..cf0b6dd703c76b3a904c4322b47b49b9b11ee5fc 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.72 2000/08/21 17:22:34 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.73 2000/08/24 03:29:05 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1447,7 +1447,6 @@ simplify_op_or_func(Expr *expr, List *args) bool has_nonconst_input = false; bool has_null_input = false; bool const_is_null; - bool isDone; /* * Check for constant inputs and especially constant-NULL inputs. @@ -1566,8 +1565,7 @@ simplify_op_or_func(Expr *expr, List *args) econtext = MakeExprContext(NULL, CurrentMemoryContext); const_val = ExecEvalExprSwitchContext((Node *) newexpr, econtext, - &const_is_null, &isDone); - Assert(isDone); /* if this isn't set, we blew it... */ + &const_is_null, NULL); /* Must copy result out of sub-context used by expression eval */ const_val = datumCopy(const_val, resultTypByVal, resultTypLen); diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c index 551e7d2b585510092c12ba8fd244f620c0d7162f..bad9401c609f9118525c006b425477c6a1bf50eb 100644 --- a/src/backend/parser/parse_func.c +++ b/src/backend/parser/parse_func.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.88 2000/08/20 00:44:18 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.89 2000/08/24 03:29:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -560,13 +560,13 @@ ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs, { /* we know all of these fields already */ /* - * We create a funcnode with a placeholder function SetEval. - * SetEval() never actually gets executed. When the function - * evaluation routines see it, they use the funcid projected out - * from the relation as the actual function to call. Example: - * retrieve (emp.mgr.name) The plan for this will scan the emp - * relation, projecting out the mgr attribute, which is a funcid. - * This function is then called (instead of SetEval) and "name" is + * We create a funcnode with a placeholder function seteval(). + * At runtime, seteval() will execute the function identified + * by the funcid it receives as parameter. + * + * Example: retrieve (emp.mgr.name). The plan for this will scan the + * emp relation, projecting out the mgr attribute, which is a funcid. + * This function is then called (via seteval()) and "name" is * projected from its result. */ funcid = F_SETEVAL; diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c index 2bda101538ce88af27123b4df1649b75e393d97e..5d363ea3e690d085a5545a816d629ee1ae873feb 100644 --- a/src/backend/parser/parse_node.c +++ b/src/backend/parser/parse_node.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.44 2000/08/08 15:42:04 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.45 2000/08/24 03:29:05 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -34,7 +34,6 @@ #include "utils/lsyscache.h" #include "utils/syscache.h" -static void disallow_setop(char *op, Type optype, Node *operand); static bool fitsInFloat(Value *value); @@ -71,7 +70,6 @@ make_operand(char *opname, if (tree != NULL) { - disallow_setop(opname, target_type, tree); /* must coerce? */ if (target_typeId != orig_typeId) result = coerce_type(NULL, tree, orig_typeId, target_typeId, -1); @@ -96,21 +94,6 @@ make_operand(char *opname, } /* make_operand() */ -static void -disallow_setop(char *op, Type optype, Node *operand) -{ - if (operand == NULL) - return; - - if (nodeTag(operand) == T_Iter) - { - elog(ERROR, "An operand to the '%s' operator returns a set of %s," - "\n\tbut '%s' takes single values, not sets.", - op, typeTypeName(optype), op); - } -} - - /* make_op() * Operator construction. * diff --git a/src/backend/utils/adt/sets.c b/src/backend/utils/adt/sets.c index a0c0aa8cb14e3738e13119f483ff61b2ab7d5f3a..9a5f05134cd45f275b130f8d11ac16cdd5fd9578 100644 --- a/src/backend/utils/adt/sets.c +++ b/src/backend/utils/adt/sets.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.32 2000/06/09 01:11:09 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/adt/Attic/sets.c,v 1.33 2000/08/24 03:29:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,8 @@ #include "catalog/catname.h" #include "catalog/indexing.h" #include "catalog/pg_proc.h" +#include "executor/executor.h" +#include "utils/fcache.h" #include "utils/sets.h" #include "utils/syscache.h" @@ -30,9 +32,9 @@ extern CommandDest whereToSendOutput; /* defined in tcop/postgres.c */ /* * SetDefine - converts query string defining set to an oid * - * The query string is used to store the set as a function in - * pg_proc. The name of the function is then changed to use the - * OID of its tuple in pg_proc. + * We create an SQL function having the given querystring as its body. + * The name of the function is then changed to use the OID of its tuple + * in pg_proc. */ Oid SetDefine(char *querystr, char *typename) @@ -57,11 +59,11 @@ SetDefine(char *querystr, char *typename) querystr, /* sourceCode */ fileName, /* fileName */ true, /* trusted */ - false, /* canCache XXX appropriate? */ - false, /* isStrict XXX appropriate? */ + false, /* canCache (assume unsafe) */ + false, /* isStrict (irrelevant, no args) */ 100, /* byte_pct */ - 0, /* perbyte_cpu */ - 0, /* percall_cpu */ + 0, /* perbyte_cpu */ + 0, /* percall_cpu */ 100, /* outin_ratio */ NIL, /* argList */ whereToSendOutput); @@ -74,11 +76,12 @@ SetDefine(char *querystr, char *typename) * until you start the next command.) */ CommandCounterIncrement(); + tup = SearchSysCacheTuple(PROCOID, ObjectIdGetDatum(setoid), 0, 0, 0); if (!HeapTupleIsValid(tup)) - elog(ERROR, "setin: unable to define set %s", querystr); + elog(ERROR, "SetDefine: unable to define set %s", querystr); /* * We can tell whether the set was already defined by checking the @@ -86,7 +89,7 @@ SetDefine(char *querystr, char *typename) * oid>" it's already defined. */ proc = (Form_pg_proc) GETSTRUCT(tup); - if (!strcmp((char *) procname, (char *) &(proc->proname))) + if (strcmp(procname, NameStr(proc->proname)) == 0) { /* make the real proc name */ sprintf(realprocname, "set%u", setoid); @@ -120,7 +123,7 @@ SetDefine(char *querystr, char *typename) setoid = newtup->t_data->t_oid; } else - elog(ERROR, "setin: could not find new set oid tuple"); + elog(ERROR, "SetDefine: could not find new set oid tuple"); if (RelationGetForm(procrel)->relhasindex) { @@ -132,20 +135,79 @@ SetDefine(char *querystr, char *typename) } heap_close(procrel, RowExclusiveLock); } + return setoid; } -/* This function is a placeholder. The parser uses the OID of this - * function to fill in the :funcid field of a set. This routine is - * never executed. At runtime, the OID of the actual set is substituted - * into the :funcid. +/* + * This function executes set evaluation. The parser sets up a set reference + * as a call to this function with the OID of the set to evaluate as argument. + * + * We build a new fcache for execution of the set's function and run the + * function until it says "no mas". The fn_extra field of the call's + * FmgrInfo record is a handy place to hold onto the fcache. (Since this + * is a built-in function, there is no competing use of fn_extra.) */ Datum seteval(PG_FUNCTION_ARGS) { Oid funcoid = PG_GETARG_OID(0); + FunctionCachePtr fcache; + Datum result; + bool isNull; + ExprDoneCond isDone; + + /* + * If this is the first call, we need to set up the fcache for the + * target set's function. + */ + fcache = (FunctionCachePtr) fcinfo->flinfo->fn_extra; + if (fcache == NULL) + { + fcache = init_fcache(funcoid, 0, fcinfo->flinfo->fn_mcxt); + fcinfo->flinfo->fn_extra = (void *) fcache; + } + + /* + * Evaluate the function. NOTE: we need no econtext because there + * are no arguments to evaluate. + */ + + /* ExecMakeFunctionResult assumes these are initialized at call: */ + isNull = false; + isDone = ExprSingleResult; - elog(ERROR, "seteval called for OID %u", funcoid); + result = ExecMakeFunctionResult(fcache, + NIL, + NULL, /* no econtext, see above */ + &isNull, + &isDone); + + /* + * If we're done with the results of this set function, get rid of + * its func cache so that we will start from the top next time. + * (Can you say "memory leak"? This feature is a crock anyway...) + */ + if (isDone != ExprMultipleResult) + { + pfree(fcache); + fcinfo->flinfo->fn_extra = NULL; + } + + /* + * Return isNull/isDone status. + */ + fcinfo->isnull = isNull; + + if (isDone != ExprSingleResult) + { + ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; + + if (rsi && IsA(rsi, ReturnSetInfo)) + rsi->isDone = isDone; + else + elog(ERROR, "Set-valued function called in context that cannot accept a set"); + } - PG_RETURN_INT32(0); /* keep compiler happy */ + PG_RETURN_DATUM(result); } diff --git a/src/backend/utils/cache/fcache.c b/src/backend/utils/cache/fcache.c index 080f70b6ce8fd5d9e4444a42be7a5879558faff0..9186f34d104737af5144649f8eb71b7f42ef4ca2 100644 --- a/src/backend/utils/cache/fcache.c +++ b/src/backend/utils/cache/fcache.c @@ -1,267 +1,61 @@ /*------------------------------------------------------------------------- * * fcache.c - * Code for the 'function cache' used in Oper and Func nodes.... + * Code for the 'function cache' used in Oper and Func nodes. + * * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.36 2000/08/11 18:35:50 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/cache/Attic/fcache.c,v 1.37 2000/08/24 03:29:07 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" -#include "access/heapam.h" -#include "catalog/pg_language.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" -#include "parser/parsetree.h" -#include "utils/builtins.h" -#include "utils/fcache2.h" -#include "utils/syscache.h" - -static Oid GetDynamicFuncArgType(Var *arg, ExprContext *econtext); -static FunctionCachePtr init_fcache(Oid foid, - List *argList, - ExprContext *econtext); - -#define FuncArgTypeIsDynamic(arg) \ - (IsA(arg,Var) && ((Var*)arg)->varattno == InvalidAttrNumber) - -static Oid -GetDynamicFuncArgType(Var *arg, ExprContext *econtext) -{ - char *relname; - int rtid; - HeapTuple tup; - - Assert(IsA(arg, Var)); +#include "utils/fcache.h" - rtid = ((Var *) arg)->varno; - relname = (char *) getrelname(rtid, econtext->ecxt_range_table); - - tup = SearchSysCacheTuple(TYPENAME, - PointerGetDatum(relname), - 0, 0, 0); - if (!tup) - elog(ERROR, "Lookup failed on type tuple for class %s", - relname); - - return tup->t_data->t_oid; -} /*----------------------------------------------------------------- * - * Initialize a 'FunctionCache' struct given the PG_PROC oid. + * Build a 'FunctionCache' struct given the PG_PROC oid. * *----------------------------------------------------------------- */ -static FunctionCachePtr -init_fcache(Oid foid, - List *argList, - ExprContext *econtext) +FunctionCachePtr +init_fcache(Oid foid, int nargs, MemoryContext fcacheCxt) { - HeapTuple procedureTuple; - HeapTuple typeTuple; - Form_pg_proc procedureStruct; - Form_pg_type typeStruct; + MemoryContext oldcontext; FunctionCachePtr retval; - int nargs; - Datum tmp; - bool isNull; + + /* Switch to a context long-lived enough for the fcache entry */ + oldcontext = MemoryContextSwitchTo(fcacheCxt); retval = (FunctionCachePtr) palloc(sizeof(FunctionCache)); MemSet(retval, 0, sizeof(FunctionCache)); - retval->fcacheCxt = CurrentMemoryContext; - - /* ---------------- - * get the procedure tuple corresponding to the given functionOid - * - * NB: use SearchSysCacheTupleCopy to ensure tuple lives long enough - * ---------------- - */ - procedureTuple = SearchSysCacheTupleCopy(PROCOID, - ObjectIdGetDatum(foid), - 0, 0, 0); - if (!HeapTupleIsValid(procedureTuple)) - elog(ERROR, "init_fcache: Cache lookup failed for procedure %u", - foid); + /* Set up the primary fmgr lookup information */ + fmgr_info(foid, &(retval->func)); - procedureStruct = (Form_pg_proc) GETSTRUCT(procedureTuple); + /* Initialize unvarying fields of per-call info block */ + retval->fcinfo.flinfo = &(retval->func); + retval->fcinfo.nargs = nargs; - /* ---------------- - * get the return type from the procedure tuple - * ---------------- - */ - typeTuple = SearchSysCacheTuple(TYPEOID, - ObjectIdGetDatum(procedureStruct->prorettype), - 0, 0, 0); + if (nargs > FUNC_MAX_ARGS) + elog(ERROR, "init_fcache: too many arguments"); - if (!HeapTupleIsValid(typeTuple)) - elog(ERROR, "init_fcache: Cache lookup failed for type %u", - procedureStruct->prorettype); - - typeStruct = (Form_pg_type) GETSTRUCT(typeTuple); - - /* ---------------- - * get the type length and by-value flag from the type tuple - * ---------------- - */ - retval->typlen = typeStruct->typlen; - if (typeStruct->typrelid == InvalidOid) + /* If function returns set, prepare a resultinfo node for communication */ + if (retval->func.fn_retset) { - /* The return type is not a relation, so just use byval */ - retval->typbyval = typeStruct->typbyval; - retval->returnsTuple = false; + retval->fcinfo.resultinfo = (Node *) &(retval->rsinfo); + retval->rsinfo.type = T_ReturnSetInfo; } - else - { - /* - * This is a hack. We assume here that any function returning a - * tuple returns it by reference. This needs to be fixed, since - * actually the mechanism isn't quite like return-by-reference. - */ - retval->typbyval = false; - retval->returnsTuple = true; - } - retval->foid = foid; - retval->language = procedureStruct->prolang; - retval->returnsSet = procedureStruct->proretset; + retval->argsValid = false; retval->hasSetArg = false; - retval->func_state = (char *) NULL; - retval->setArg = (Datum) 0; - - /* - * If we are returning exactly one result then we have to copy tuples - * and by reference results because we have to end the execution - * before we return the results. When you do this everything - * allocated by the executor (i.e. slots and tuples) is freed. - */ - if ((retval->language == SQLlanguageId) && - !retval->returnsSet && - !retval->typbyval) - { - TupleTableSlot *slot; - - slot = makeNode(TupleTableSlot); - slot->ttc_shouldFree = true; - slot->ttc_descIsNew = true; - slot->ttc_tupleDescriptor = (TupleDesc) NULL; - slot->ttc_buffer = InvalidBuffer; - slot->ttc_whichplan = -1; - - retval->funcSlot = (Pointer) slot; - } - else - retval->funcSlot = (Pointer) NULL; - - nargs = procedureStruct->pronargs; - retval->nargs = nargs; - - if (nargs > 0) - { - Oid *argTypes; - - if (retval->language == SQLlanguageId) - { - int i; - List *oneArg; - - retval->argOidVect = (Oid *) palloc(retval->nargs * sizeof(Oid)); - argTypes = procedureStruct->proargtypes; - memmove(retval->argOidVect, - argTypes, - (retval->nargs) * sizeof(Oid)); - for (i = 0; - argList; - i++, argList = lnext(argList)) - { - oneArg = lfirst(argList); - if (FuncArgTypeIsDynamic(oneArg)) - retval->argOidVect[i] = GetDynamicFuncArgType((Var *) oneArg, - econtext); - } - } - else - retval->argOidVect = (Oid *) NULL; - } - else - { - retval->argOidVect = (Oid *) NULL; - } - - if (procedureStruct->prolang == SQLlanguageId) - { - tmp = SysCacheGetAttr(PROCOID, - procedureTuple, - Anum_pg_proc_prosrc, - &isNull); - if (isNull) - elog(ERROR, "init_fcache: null prosrc for procedure %u", - foid); - retval->src = DatumGetCString(DirectFunctionCall1(textout, tmp)); - retval->bin = (char *) NULL; - } - else - { - retval->src = (char *) NULL; - if (procedureStruct->proistrusted) - retval->bin = (char *) NULL; - else - { - tmp = SysCacheGetAttr(PROCOID, - procedureTuple, - Anum_pg_proc_probin, - &isNull); - if (isNull) - elog(ERROR, "init_fcache: null probin for procedure %u", - foid); - retval->bin = DatumGetCString(DirectFunctionCall1(textout, tmp)); - } - } - - if (retval->language != SQLlanguageId) - { - fmgr_info(foid, &(retval->func)); - retval->nargs = retval->func.fn_nargs; - } - else - retval->func.fn_addr = (PGFunction) NULL; - - heap_freetuple(procedureTuple); + MemoryContextSwitchTo(oldcontext); return retval; } - -void -setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext) -{ - MemoryContext oldcontext; - FunctionCachePtr fcache; - - /* Switch to a context long-lived enough for the fcache entry */ - oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); - - fcache = init_fcache(foid, argList, econtext); - - if (IsA(node, Oper)) - { - Oper *onode = (Oper *) node; - onode->op_fcache = fcache; - } - else if (IsA(node, Func)) - { - Func *fnode = (Func *) node; - fnode->func_fcache = fcache; - } - else - elog(ERROR, "init_fcache: node must be Oper or Func!"); - - MemoryContextSwitchTo(oldcontext); -} diff --git a/src/backend/utils/fmgr/fmgr.c b/src/backend/utils/fmgr/fmgr.c index 7b8aca3feb4a6887aa400ea9d98b0d35824b71f8..c7fbf251f9d96b6d52d591be6f5c4af852c20edf 100644 --- a/src/backend/utils/fmgr/fmgr.c +++ b/src/backend/utils/fmgr/fmgr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.45 2000/07/06 05:48:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/utils/fmgr/fmgr.c,v 1.46 2000/08/24 03:29:07 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "catalog/pg_language.h" #include "catalog/pg_proc.h" +#include "executor/functions.h" #include "utils/builtins.h" #include "utils/fmgrtab.h" #include "utils/syscache.h" @@ -44,7 +45,6 @@ typedef char *((*func_ptr) ()); static Datum fmgr_oldstyle(PG_FUNCTION_ARGS); static Datum fmgr_untrusted(PG_FUNCTION_ARGS); -static Datum fmgr_sql(PG_FUNCTION_ARGS); /* @@ -111,6 +111,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) finfo->fn_oid = functionId; finfo->fn_extra = NULL; + finfo->fn_mcxt = CurrentMemoryContext; if ((fbp = fmgr_isbuiltin(functionId)) != NULL) { @@ -119,6 +120,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) */ finfo->fn_nargs = fbp->nargs; finfo->fn_strict = fbp->strict; + finfo->fn_retset = false; /* assume no builtins return sets! */ if (fbp->oldstyle) { finfo->fn_addr = fmgr_oldstyle; @@ -142,6 +144,7 @@ fmgr_info(Oid functionId, FmgrInfo *finfo) finfo->fn_nargs = procedureStruct->pronargs; finfo->fn_strict = procedureStruct->proisstrict; + finfo->fn_retset = procedureStruct->proretset; if (!procedureStruct->proistrusted) { @@ -427,21 +430,6 @@ fmgr_untrusted(PG_FUNCTION_ARGS) return 0; /* keep compiler happy */ } -/* - * Handler for SQL-language functions - */ -static Datum -fmgr_sql(PG_FUNCTION_ARGS) -{ - /* - * XXX It'd be really nice to support SQL functions anywhere that - * builtins are supported. What would we have to do? What pitfalls - * are there? - */ - elog(ERROR, "SQL-language function not supported in this context"); - return 0; /* keep compiler happy */ -} - /*------------------------------------------------------------------------- * Support routines for callers of fmgr-compatible functions diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index b50f74fdf4fba9b8d46074f58ad26e02dae2f215..c7e092c7020fec5a75f4518ba29c4a136c698439 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pg_proc.h,v 1.163 2000/08/23 06:04:44 thomas Exp $ + * $Id: pg_proc.h,v 1.164 2000/08/24 03:29:08 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1266,8 +1266,8 @@ DATA(insert OID = 1036 ( aclremove PGUID 12 f t f t 2 f 1034 "1034 1033" 10 DESCR("remove ACL item"); DATA(insert OID = 1037 ( aclcontains PGUID 12 f t f t 2 f 16 "1034 1033" 100 0 0 100 aclcontains - )); DESCR("does ACL contain item?"); -DATA(insert OID = 1038 ( seteval PGUID 12 f t f t 1 f 23 "26" 100 0 0 100 seteval - )); -DESCR(""); +DATA(insert OID = 1038 ( seteval PGUID 12 f t f t 1 t 23 "26" 100 0 0 100 seteval - )); +DESCR("internal function supporting PostQuel-style sets"); DATA(insert OID = 1044 ( bpcharin PGUID 12 f t t t 3 f 1042 "0 26 23" 100 0 0 100 bpcharin - )); DESCR("(internal)"); DATA(insert OID = 1045 ( bpcharout PGUID 12 f t t t 1 f 23 "0" 100 0 0 100 bpcharout - )); diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h index f0a1e381e1ae383a8ea686eb92b0fe15981a0dd1..2a7a4bdcfa82464f297871162df9275581022b1a 100644 --- a/src/include/commands/defrem.h +++ b/src/include/commands/defrem.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: defrem.h,v 1.19 2000/02/18 09:29:49 inoue Exp $ + * $Id: defrem.h,v 1.20 2000/08/24 03:29:09 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -44,7 +44,6 @@ extern void CreateFunction(ProcedureStmt *stmt, CommandDest dest); extern void DefineOperator(char *name, List *parameters); extern void DefineAggregate(char *name, List *parameters); extern void DefineType(char *name, List *parameters); -extern void CreateFunction(ProcedureStmt *stmt, CommandDest dest); /* * prototypes in remove.c diff --git a/src/include/executor/execFlatten.h b/src/include/executor/execFlatten.h index c7d85e2e6da4282b5dc564efcaefe7a443688dc9..3fa3673b201de37bd695a2d53c60d3ed9dcc54c8 100644 --- a/src/include/executor/execFlatten.h +++ b/src/include/executor/execFlatten.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execFlatten.h,v 1.11 2000/01/26 05:58:05 momjian Exp $ + * $Id: execFlatten.h,v 1.12 2000/08/24 03:29:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -15,11 +15,14 @@ #define EXECFLATTEN_H #include "nodes/execnodes.h" -#include "nodes/relation.h" +#include "nodes/parsenodes.h" -extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, bool *resultIsNull, bool *iterIsDone); -extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, bool *isNullVect, bool *fj_isDone); +extern Datum ExecEvalIter(Iter *iterNode, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); + +extern void ExecEvalFjoin(TargetEntry *tlist, ExprContext *econtext, + bool *isNullVect, ExprDoneCond *fj_isDone); #endif /* EXECFLATTEN_H */ diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index ea589e06dd95e9f8f9555aa56a59f8e21c4de3fc..e39a60a6a2478140b6d7db997e766264e2e49f3b 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: executor.h,v 1.48 2000/08/21 20:55:29 tgl Exp $ + * $Id: executor.h,v 1.49 2000/08/24 03:29:10 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -78,14 +78,20 @@ extern char *GetAttributeByNum(TupleTableSlot *slot, AttrNumber attrno, bool *isNull); extern char *GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull); +extern Datum ExecMakeFunctionResult(FunctionCachePtr fcache, + List *arguments, + ExprContext *econtext, + bool *isNull, + ExprDoneCond *isDone); extern Datum ExecEvalExpr(Node *expression, ExprContext *econtext, - bool *isNull, bool *isDone); + bool *isNull, ExprDoneCond *isDone); extern Datum ExecEvalExprSwitchContext(Node *expression, ExprContext *econtext, - bool *isNull, bool *isDone); + bool *isNull, ExprDoneCond *isDone); extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, bool *isDone); +extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, + ExprDoneCond *isDone); /* * prototypes from functions in execScan.c diff --git a/src/include/executor/functions.h b/src/include/executor/functions.h index 0e55be1980ed24fc6d6a9a1bfbe605ffe45a68f3..649e38e142f2360251012b3566d53549e9eb89ad 100644 --- a/src/include/executor/functions.h +++ b/src/include/executor/functions.h @@ -1,24 +1,21 @@ /*------------------------------------------------------------------------- * * functions.h - * + * Declarations for execution of SQL-language functions. * * * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: functions.h,v 1.13 2000/08/08 15:42:39 tgl Exp $ + * $Id: functions.h,v 1.14 2000/08/24 03:29:10 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef FUNCTIONS_H #define FUNCTIONS_H -#include "nodes/parsenodes.h" -#include "utils/syscache.h" +#include "fmgr.h" -extern Datum postquel_function(FunctionCallInfo fcinfo, - FunctionCachePtr fcache, - bool *isDone); +extern Datum fmgr_sql(PG_FUNCTION_ARGS); #endif /* FUNCTIONS_H */ diff --git a/src/include/fmgr.h b/src/include/fmgr.h index 274f8f959d86edde2e4ff9e3bf62ce9c4aadbccf..28634262bcc13a964342cb652a23bcfb1058dd93 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fmgr.h,v 1.9 2000/07/29 03:26:47 tgl Exp $ + * $Id: fmgr.h,v 1.10 2000/08/24 03:29:11 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,7 +41,9 @@ typedef struct Oid fn_oid; /* OID of function (NOT of handler, if any) */ short fn_nargs; /* 0..FUNC_MAX_ARGS, or -1 if variable arg count */ bool fn_strict; /* function is "strict" (NULL in => NULL out) */ + bool fn_retset; /* function returns a set (over multiple calls) */ void *fn_extra; /* extra space for use by handler */ + MemoryContext fn_mcxt; /* memory context to store fn_extra in */ } FmgrInfo; /* diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 1ec14f4a9697d35d758ae4b25380b5af9cad950f..9626dbf8b1c0c658122af32860d5ed3118b0c5b9 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: execnodes.h,v 1.47 2000/08/22 04:06:22 tgl Exp $ + * $Id: execnodes.h,v 1.48 2000/08/24 03:29:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -120,6 +120,31 @@ typedef struct ExprContext List *ecxt_range_table; } ExprContext; +/* + * Set-result status returned by ExecEvalExpr() + */ +typedef enum +{ + ExprSingleResult, /* expression does not return a set */ + ExprMultipleResult, /* this result is an element of a set */ + ExprEndResult /* there are no more elements in the set */ +} ExprDoneCond; + +/* + * When calling a function that might return a set (multiple rows), + * a node of this type is passed as fcinfo->resultinfo to allow + * return status to be passed back. A function returning set should + * raise an error if no such resultinfo is provided. + * + * XXX this mechanism is a quick hack and probably needs to be redesigned. + */ +typedef struct ReturnSetInfo +{ + NodeTag type; + ExprDoneCond isDone; +} ReturnSetInfo; + + /* ---------------- * ProjectionInfo node information * diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f05e0a0e1c93b784b9ee1c23bcbfd9890011ff8b..d825c8fe395bcd8f64f4d191277884213608104e 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: nodes.h,v 1.74 2000/08/11 23:46:54 tgl Exp $ + * $Id: nodes.h,v 1.75 2000/08/24 03:29:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -227,7 +227,9 @@ typedef enum NodeTag * TAGS FOR FUNCTION-CALL CONTEXT AND RESULTINFO NODES (cf. fmgr.h) *--------------------- */ - T_TriggerData = 800 /* in commands/trigger.h */ + T_TriggerData = 800, /* in commands/trigger.h */ + T_ReturnSetInfo /* in nodes/execnodes.h */ + } NodeTag; /* diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 1ad9a3d082ad1bd5ecfbf87771482a910fb7b692..0ef350687dc41a6466dc0192260b875c20424aba 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: primnodes.h,v 1.46 2000/08/08 15:42:59 tgl Exp $ + * $Id: primnodes.h,v 1.47 2000/08/24 03:29:13 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -16,7 +16,10 @@ #include "access/attnum.h" #include "nodes/pg_list.h" -#include "utils/fcache.h" + +/* FunctionCache is declared in utils/fcache.h */ +typedef struct FunctionCache *FunctionCachePtr; + /* ---------------------------------------------------------------- * node definitions diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 1e16af6874f5f951ad936fe818dc7a8b2f4cb1b5..f1ebe40b5907e7ad2b32dac1ace9ec50ff41be9c 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,13 +7,14 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: builtins.h,v 1.133 2000/08/23 06:04:49 thomas Exp $ + * $Id: builtins.h,v 1.134 2000/08/24 03:29:14 tgl Exp $ * *------------------------------------------------------------------------- */ #ifndef BUILTINS_H #define BUILTINS_H +#include "fmgr.h" #include "nodes/relation.h" /* for amcostestimate parameters */ #include "storage/itemptr.h" #include "utils/numeric.h" diff --git a/src/include/utils/fcache.h b/src/include/utils/fcache.h index efae7613959a75a75491483dd5fd3c91ef73d102..a30c72836085085e0fb90c2a1fa86331d93f9fcd 100644 --- a/src/include/utils/fcache.h +++ b/src/include/utils/fcache.h @@ -11,7 +11,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: fcache.h,v 1.13 2000/08/08 15:43:12 tgl Exp $ + * $Id: fcache.h,v 1.14 2000/08/24 03:29:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -19,45 +19,50 @@ #define FCACHE_H #include "fmgr.h" +#include "nodes/execnodes.h" +/* + * A FunctionCache record is built for all functions regardless of language. + * + * We store the fmgr lookup info to avoid recomputing it on each call. + * We also store a prebuilt FunctionCallInfo struct. When evaluating a + * function-returning-set, fcinfo holds the argument values across calls + * so that we need not re-evaluate the arguments for each call. Even for + * non-set functions, fcinfo saves a few cycles per call by allowing us to + * avoid redundant setup of its fields. + */ -typedef struct +typedef struct FunctionCache { - FmgrInfo func; /* info for fmgr call mechanism */ - Oid foid; /* oid of the function in pg_proc */ - Oid language; /* oid of the language in pg_language */ - int typlen; /* length of the return type */ - bool typbyval; /* true if return type is pass by value */ - - bool returnsTuple; /* true if return type is a tuple */ - bool returnsSet; /* true if func returns a set (multi rows) */ - - bool hasSetArg; /* true if func is part of a nested dot - * expr whose argument is func returning a - * set ugh! */ - - /* If additional info is added to an existing fcache, be sure to - * allocate it in the fcacheCxt. + /* + * Function manager's lookup info for the target function. */ - MemoryContext fcacheCxt; /* context the fcache lives in */ - - int nargs; /* actual number of arguments */ - Oid *argOidVect; /* oids of all the argument types */ - - char *src; /* source code of the function */ - char *bin; /* binary object code ?? */ - char *func_state; /* function_state struct for execution */ - - Pointer funcSlot; /* if one result we need to copy it before - * we end execution of the function and - * free stuff */ - - Datum setArg; /* current argument for nested dot - * execution Nested dot expressions mean - * we have funcs whose argument is a set - * of tuples */ + FmgrInfo func; + /* + * Per-call info for calling the target function. Unvarying fields + * are set up by init_fcache(). Argument values are filled in as needed. + */ + FunctionCallInfoData fcinfo; + /* + * "Resultinfo" node --- used only if target function returns a set. + */ + ReturnSetInfo rsinfo; + /* + * argsValid is true when we are evaluating a set-valued function and + * we are in the middle of a call series; we want to pass the same + * argument values to the function again (and again, until it returns + * ExprEndResult). + */ + bool argsValid; /* TRUE if fcinfo contains valid arguments */ + /* + * hasSetArg is true if we found a set-valued argument to the function. + * This causes the function result to be a set as well. + */ + bool hasSetArg; /* some argument returns a set */ } FunctionCache; -typedef FunctionCache *FunctionCachePtr; + +extern FunctionCachePtr init_fcache(Oid foid, int nargs, + MemoryContext fcacheCxt); #endif /* FCACHE_H */ diff --git a/src/include/utils/fcache2.h b/src/include/utils/fcache2.h deleted file mode 100644 index 984edde4929eef84cb6f9df8963e6bc491f34333..0000000000000000000000000000000000000000 --- a/src/include/utils/fcache2.h +++ /dev/null @@ -1,21 +0,0 @@ -/*------------------------------------------------------------------------- - * - * fcache2.h - * - * - * - * Portions Copyright (c) 1996-2000, PostgreSQL, Inc - * Portions Copyright (c) 1994, Regents of the University of California - * - * $Id: fcache2.h,v 1.10 2000/01/26 05:58:38 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef FCACHE2_H -#define FCACHE2_H - -#include "nodes/execnodes.h" - -extern void setFcache(Node *node, Oid foid, List *argList, ExprContext *econtext); - -#endif /* FCACHE2_H */ diff --git a/src/include/utils/sets.h b/src/include/utils/sets.h index a7b5d6826e2bc53087ffdea921eb7dae28353bc8..7f284ac3467d3f13bbf2cf07291f86dd7e9dd1e3 100644 --- a/src/include/utils/sets.h +++ b/src/include/utils/sets.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: sets.h,v 1.7 2000/06/09 01:11:15 tgl Exp $ + * $Id: sets.h,v 1.8 2000/08/24 03:29:14 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,10 +17,11 @@ #include "fmgr.h" -/* Temporary name of set, before SetDefine changes it. */ -#define GENERICSETNAME "zyxset" +/* Temporary name of a set function, before SetDefine changes it. */ +#define GENERICSETNAME "ZYX#Set#ZYX" extern Oid SetDefine(char *querystr, char *typename); + extern Datum seteval(PG_FUNCTION_ARGS); #endif /* SETS_H */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 0096b3825c63019e0aac39c97e3bb0b0d285a401..6c761c50f0c843e93cc8d4914c2051090b2a3408 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.27 2000/08/13 02:50:35 tgl Exp $ + * $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.28 2000/08/24 03:29:15 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -2200,7 +2200,6 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, int fno; int i; bool isnull; - bool isdone; ExprContext *econtext; ParamListInfo paramLI; @@ -2274,9 +2273,7 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, * Initialize things * ---------- */ - *isNull = FALSE; *rettype = expr->plan_simple_type; - isdone = FALSE; /* ---------- * Clear the function cache @@ -2292,14 +2289,17 @@ exec_eval_simple_expr(PLpgSQL_execstate * estate, retval = ExecEvalExprSwitchContext(expr->plan_simple_expr, econtext, isNull, - &isdone); + NULL); SPI_pop(); /* * Copy the result out of the expression-evaluation memory context, * so that we can free the expression context. */ - retval = datumCopy(retval, get_typbyval(*rettype), get_typlen(*rettype)); + if (! *isNull) + retval = datumCopy(retval, + get_typbyval(*rettype), + get_typlen(*rettype)); FreeExprContext(econtext);