diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2002-12-13 19:46:01 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2002-12-13 19:46:01 +0000 |
commit | 3a4f7dde16ad81b2319b9a4924a6023710a2fefd (patch) | |
tree | 248cf66fd94d40072b5ba8bb8e5437a6ea8399e5 /src/backend/executor/execQual.c | |
parent | 77b7a740f95250af7d78f69e9c906c3e53f32e7b (diff) | |
download | postgresql-3a4f7dde16ad81b2319b9a4924a6023710a2fefd.tar.gz postgresql-3a4f7dde16ad81b2319b9a4924a6023710a2fefd.zip |
Phase 3 of read-only-plans project: ExecInitExpr now builds expression
execution state trees, and ExecEvalExpr takes an expression state tree
not an expression plan tree. The plan tree is now read-only as far as
the executor is concerned. Next step is to begin actually exploiting
this property.
Diffstat (limited to 'src/backend/executor/execQual.c')
-rw-r--r-- | src/backend/executor/execQual.c | 658 |
1 files changed, 400 insertions, 258 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index 79796f1c0b2..b529d045c4d 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.118 2002/12/12 20:35:12 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.119 2002/12/13 19:45:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -41,43 +41,52 @@ #include "executor/nodeSubplan.h" #include "miscadmin.h" #include "parser/parse_expr.h" +#include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" -#include "utils/fcache.h" #include "utils/lsyscache.h" /* static function decls */ -static Datum ExecEvalAggref(Aggref *aggref, ExprContext *econtext, - bool *isNull); -static Datum ExecEvalArrayRef(ArrayRef *arrayRef, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalAggref(AggrefExprState *aggref, + ExprContext *econtext, + bool *isNull); +static Datum ExecEvalArrayRef(ArrayRefExprState *astate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOper(OpExpr *op, ExprContext *econtext, +static Datum ExecEvalParam(Param *expression, ExprContext *econtext, + bool *isNull); +static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalDistinct(DistinctExpr *op, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalFunc(FuncExpr *func, ExprContext *econtext, +static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext); -static Datum ExecEvalNot(BoolExpr *notclause, ExprContext *econtext, +static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull); -static Datum ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, +static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, +static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull); -static Datum ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, +static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalNullTest(NullTest *ntest, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalBooleanTest(BooleanTest *btest, ExprContext *econtext, - bool *isNull, ExprDoneCond *isDone); -static Datum ExecEvalConstraintTest(ConstraintTest *constraint, +static Datum ExecEvalNullTest(GenericExprState *nstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalBooleanTest(GenericExprState *bstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); +static Datum ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext, bool *isNull); +static Datum ExecEvalFieldSelect(GenericExprState *fstate, + ExprContext *econtext, + bool *isNull, ExprDoneCond *isDone); /*---------- @@ -106,11 +115,12 @@ static Datum ExecEvalConstraintTestValue(ConstraintTestValue *conVal, *---------- */ static Datum -ExecEvalArrayRef(ArrayRef *arrayRef, +ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr; ArrayType *array_source; ArrayType *resultArray; bool isAssignment = (arrayRef->refassgnexpr != NULL); @@ -124,7 +134,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, if (arrayRef->refexpr != NULL) { array_source = (ArrayType *) - DatumGetPointer(ExecEvalExpr((Node *) arrayRef->refexpr, + DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); @@ -150,13 +160,13 @@ ExecEvalArrayRef(ArrayRef *arrayRef, array_source = NULL; } - foreach(elt, arrayRef->refupperindexpr) + foreach(elt, astate->refupperindexpr) { if (i >= MAXDIM) elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions", MAXDIM); - upper.indx[i++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), + upper.indx[i++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt), econtext, isNull, NULL)); @@ -170,15 +180,15 @@ ExecEvalArrayRef(ArrayRef *arrayRef, } } - if (arrayRef->reflowerindexpr != NIL) + if (astate->reflowerindexpr != NIL) { - foreach(elt, arrayRef->reflowerindexpr) + foreach(elt, astate->reflowerindexpr) { if (j >= MAXDIM) elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions", MAXDIM); - lower.indx[j++] = DatumGetInt32(ExecEvalExpr((Node *) lfirst(elt), + lower.indx[j++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt), econtext, isNull, NULL)); @@ -205,7 +215,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, if (isAssignment) { - Datum sourceData = ExecEvalExpr((Node *) arrayRef->refassgnexpr, + Datum sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, isNull, NULL); @@ -275,7 +285,7 @@ ExecEvalArrayRef(ArrayRef *arrayRef, * ---------------------------------------------------------------- */ static Datum -ExecEvalAggref(Aggref *aggref, ExprContext *econtext, bool *isNull) +ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull) { if (econtext->ecxt_aggvalues == NULL) /* safety check */ elog(ERROR, "ExecEvalAggref: no aggregates in this expression context"); @@ -403,7 +413,7 @@ ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull) * (in which case we could return NULL)? -cim 10/13/89 * ---------------------------------------------------------------- */ -Datum +static Datum ExecEvalParam(Param *expression, ExprContext *econtext, bool *isNull) { int thisParamKind = expression->paramkind; @@ -575,6 +585,30 @@ GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull) } /* + * init_fcache - initialize a FuncExprState node during first use + */ +void +init_fcache(Oid foid, FuncExprState *fcache, MemoryContext fcacheCxt) +{ + AclResult aclresult; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, get_func_name(foid)); + + /* Safety check (should never fail, as parser should check sooner) */ + if (length(fcache->args) > FUNC_MAX_ARGS) + elog(ERROR, "init_fcache: too many arguments"); + + /* Set up the primary fmgr lookup information */ + fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); + + /* Initialize additional info */ + fcache->setArgsValid = false; +} + +/* * Evaluate arguments for a function. */ static ExprDoneCond @@ -593,7 +627,7 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo, { ExprDoneCond thisArgIsDone; - fcinfo->arg[i] = ExecEvalExpr((Node *) lfirst(arg), + fcinfo->arg[i] = ExecEvalExpr((ExprState *) lfirst(arg), econtext, &fcinfo->argnull[i], &thisArgIsDone); @@ -624,12 +658,12 @@ ExecEvalFuncArgs(FunctionCallInfo fcinfo, * Evaluate the arguments to a function and then the function itself. */ Datum -ExecMakeFunctionResult(FunctionCachePtr fcache, - List *arguments, +ExecMakeFunctionResult(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + List *arguments = fcache->args; Datum result; FunctionCallInfoData fcinfo; ReturnSetInfo rsinfo; /* for functions returning sets */ @@ -823,7 +857,7 @@ ExecMakeFunctionResult(FunctionCachePtr fcache, * object. (If function returns an empty set, we just return NULL instead.) */ Tuplestorestate * -ExecMakeTableFunctionResult(Node *funcexpr, +ExecMakeTableFunctionResult(ExprState *funcexpr, ExprContext *econtext, TupleDesc expectedDesc, TupleDesc *returnDesc) @@ -841,7 +875,7 @@ ExecMakeTableFunctionResult(Node *funcexpr, bool returnsTuple = false; /* - * Normally the passed expression tree will be a FuncExpr, since the + * Normally the passed expression tree will be a FuncExprState, since the * grammar only allows a function call at the top level of a table * function reference. However, if the function doesn't return set then * the planner might have replaced the function call via constant-folding @@ -850,11 +884,10 @@ ExecMakeTableFunctionResult(Node *funcexpr, * we don't get a chance to pass a special ReturnSetInfo to any functions * buried in the expression. */ - if (funcexpr && IsA(funcexpr, FuncExpr)) + if (funcexpr && IsA(funcexpr, FuncExprState) && + IsA(funcexpr->expr, FuncExpr)) { - FuncExpr *func = (FuncExpr *) funcexpr; - List *argList; - FunctionCachePtr fcache; + FuncExprState *fcache = (FuncExprState *) funcexpr; ExprDoneCond argDone; /* @@ -862,19 +895,14 @@ ExecMakeTableFunctionResult(Node *funcexpr, */ direct_function_call = true; - funcrettype = func->funcresulttype; - argList = func->args; - /* - * get the fcache from the FuncExpr node. If it is NULL, then - * initialize it + * Initialize function cache if first time through */ - fcache = func->func_fcache; - if (fcache == NULL) + if (fcache->func.fn_oid == InvalidOid) { - fcache = init_fcache(func->funcid, length(argList), - econtext->ecxt_per_query_memory); - func->func_fcache = fcache; + FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; + + init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory); } /* @@ -887,7 +915,7 @@ ExecMakeTableFunctionResult(Node *funcexpr, */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); - argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext); + argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext); /* We don't allow sets in the arguments of the table function */ if (argDone != ExprSingleResult) elog(ERROR, "Set-valued function called in context that cannot accept a set"); @@ -914,9 +942,10 @@ ExecMakeTableFunctionResult(Node *funcexpr, { /* Treat funcexpr as a generic expression */ direct_function_call = false; - funcrettype = exprType(funcexpr); } + funcrettype = exprType((Node *) funcexpr->expr); + /* * Prepare a resultinfo node for communication. We always do this * even if not expecting a set result, so that we can pass @@ -1087,8 +1116,8 @@ ExecMakeTableFunctionResult(Node *funcexpr, /* ---------------------------------------------------------------- - * ExecEvalOper * ExecEvalFunc + * ExecEvalOper * ExecEvalDistinct * * Evaluate the functional result of a list of arguments by calling the @@ -1097,80 +1126,49 @@ ExecMakeTableFunctionResult(Node *funcexpr, */ /* ---------------------------------------------------------------- - * ExecEvalOper + * ExecEvalFunc * ---------------------------------------------------------------- */ static Datum -ExecEvalOper(OpExpr *op, +ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { - List *argList; - FunctionCachePtr fcache; - /* - * we extract the oid of the function associated with the op and then - * pass the work onto ExecMakeFunctionResult which evaluates the - * arguments and returns the result of calling the function on the - * evaluated arguments. + * Initialize function cache if first time through */ - argList = op->args; - - /* - * get the fcache from the OpExpr node. If it is NULL, then initialize - * it - */ - fcache = op->op_fcache; - if (fcache == NULL) + if (fcache->func.fn_oid == InvalidOid) { - fcache = init_fcache(op->opfuncid, length(argList), - econtext->ecxt_per_query_memory); - op->op_fcache = fcache; + FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; + + init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory); } - return ExecMakeFunctionResult(fcache, argList, econtext, - isNull, isDone); + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } /* ---------------------------------------------------------------- - * ExecEvalFunc + * ExecEvalOper * ---------------------------------------------------------------- */ - static Datum -ExecEvalFunc(FuncExpr *func, +ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { - List *argList; - FunctionCachePtr fcache; - /* - * we extract the oid of the function associated with the func node - * and then pass the work onto ExecMakeFunctionResult which evaluates - * the arguments and returns the result of calling the function on the - * evaluated arguments. - * - * this is nearly identical to the ExecEvalOper code. + * Initialize function cache if first time through */ - argList = func->args; - - /* - * get the fcache from the FuncExpr node. If it is NULL, then initialize - * it - */ - fcache = func->func_fcache; - if (fcache == NULL) + if (fcache->func.fn_oid == InvalidOid) { - fcache = init_fcache(func->funcid, length(argList), - econtext->ecxt_per_query_memory); - func->func_fcache = fcache; + OpExpr *op = (OpExpr *) fcache->xprstate.expr; + + init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); } - return ExecMakeFunctionResult(fcache, argList, econtext, - isNull, isDone); + return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } /* ---------------------------------------------------------------- @@ -1185,34 +1183,31 @@ ExecEvalFunc(FuncExpr *func, * ---------------------------------------------------------------- */ static Datum -ExecEvalDistinct(DistinctExpr *op, +ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Datum result; - FunctionCachePtr fcache; FunctionCallInfoData fcinfo; ExprDoneCond argDone; List *argList; /* - * extract info from op + * Initialize function cache if first time through */ - argList = op->args; + if (fcache->func.fn_oid == InvalidOid) + { + DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr; + + init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); + Assert(!fcache->func.fn_retset); + } /* - * get the fcache from the DistinctExpr node. If it is NULL, then - * initialize it + * extract info from fcache */ - fcache = op->op_fcache; - if (fcache == NULL) - { - fcache = init_fcache(op->opfuncid, length(argList), - econtext->ecxt_per_query_memory); - op->op_fcache = fcache; - } - Assert(!fcache->func.fn_retset); + argList = fcache->args; /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); @@ -1260,9 +1255,9 @@ ExecEvalDistinct(DistinctExpr *op, * ---------------------------------------------------------------- */ static Datum -ExecEvalNot(BoolExpr *notclause, ExprContext *econtext, bool *isNull) +ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull) { - Node *clause; + ExprState *clause; Datum expr_value; clause = lfirst(notclause->args); @@ -1288,7 +1283,7 @@ ExecEvalNot(BoolExpr *notclause, ExprContext *econtext, bool *isNull) * ---------------------------------------------------------------- */ static Datum -ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull) +ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull) { List *clauses; List *clause; @@ -1314,7 +1309,7 @@ ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull) */ foreach(clause, clauses) { - clause_value = ExecEvalExpr((Node *) lfirst(clause), + clause_value = ExecEvalExpr((ExprState *) lfirst(clause), econtext, isNull, NULL); /* @@ -1336,7 +1331,7 @@ ExecEvalOr(BoolExpr *orExpr, ExprContext *econtext, bool *isNull) * ---------------------------------------------------------------- */ static Datum -ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull) +ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull) { List *clauses; List *clause; @@ -1356,7 +1351,7 @@ ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull) */ foreach(clause, clauses) { - clause_value = ExecEvalExpr((Node *) lfirst(clause), + clause_value = ExecEvalExpr((ExprState *) lfirst(clause), econtext, isNull, NULL); /* @@ -1383,7 +1378,7 @@ ExecEvalAnd(BoolExpr *andExpr, ExprContext *econtext, bool *isNull) * ---------------------------------------------------------------- */ static Datum -ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, +ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { List *clauses; @@ -1399,9 +1394,9 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, */ foreach(clause, clauses) { - CaseWhen *wclause = lfirst(clause); + CaseWhenState *wclause = lfirst(clause); - clause_value = ExecEvalExpr((Node *) wclause->expr, + clause_value = ExecEvalExpr(wclause->expr, econtext, isNull, NULL); @@ -1413,7 +1408,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, */ if (DatumGetBool(clause_value) && !*isNull) { - return ExecEvalExpr((Node *) wclause->result, + return ExecEvalExpr(wclause->result, econtext, isNull, isDone); @@ -1422,7 +1417,7 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, if (caseExpr->defresult) { - return ExecEvalExpr((Node *) caseExpr->defresult, + return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone); @@ -1439,14 +1434,19 @@ ExecEvalCase(CaseExpr *caseExpr, ExprContext *econtext, * ---------------------------------------------------------------- */ static Datum -ExecEvalNullTest(NullTest *ntest, +ExecEvalNullTest(GenericExprState *nstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + NullTest *ntest = (NullTest *) nstate->xprstate.expr; Datum result; - result = ExecEvalExpr((Node *) ntest->arg, econtext, isNull, isDone); + result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); + + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to check */ + switch (ntest->nulltesttype) { case IS_NULL: @@ -1479,14 +1479,19 @@ ExecEvalNullTest(NullTest *ntest, * ---------------------------------------------------------------- */ static Datum -ExecEvalBooleanTest(BooleanTest *btest, +ExecEvalBooleanTest(GenericExprState *bstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr; Datum result; - result = ExecEvalExpr((Node *) btest->arg, econtext, isNull, isDone); + result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); + + if (isDone && *isDone == ExprEndResult) + return result; /* nothing to check */ + switch (btest->booltesttype) { case IS_TRUE: @@ -1560,12 +1565,13 @@ ExecEvalBooleanTest(BooleanTest *btest, * datum) otherwise throw an error. */ static Datum -ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext, +ExecEvalConstraintTest(ConstraintTestState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + ConstraintTest *constraint = (ConstraintTest *) cstate->xprstate.expr; Datum result; - result = ExecEvalExpr((Node *) constraint->arg, econtext, isNull, isDone); + result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ @@ -1596,7 +1602,7 @@ ExecEvalConstraintTest(ConstraintTest *constraint, ExprContext *econtext, econtext->domainValue_datum = result; econtext->domainValue_isNull = *isNull; - conResult = ExecEvalExpr((Node *) constraint->check_expr, + conResult = ExecEvalExpr(cstate->check_expr, econtext, &conIsNull, NULL); if (!conIsNull && @@ -1637,17 +1643,21 @@ ExecEvalConstraintTestValue(ConstraintTestValue *conVal, ExprContext *econtext, * ---------------------------------------------------------------- */ static Datum -ExecEvalFieldSelect(FieldSelect *fselect, +ExecEvalFieldSelect(GenericExprState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { + FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr; Datum result; TupleTableSlot *resSlot; - result = ExecEvalExpr((Node *) fselect->arg, econtext, isNull, isDone); + result = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); + + /* this test covers the isDone exception too: */ if (*isNull) return result; + resSlot = (TupleTableSlot *) DatumGetPointer(result); Assert(resSlot != NULL && IsA(resSlot, TupleTableSlot)); result = heap_getattr(resSlot->val, @@ -1663,7 +1673,7 @@ ExecEvalFieldSelect(FieldSelect *fselect, * Recursively evaluate a targetlist or qualification expression. * * Inputs: - * expression: the expression tree to evaluate + * expression: the expression state tree to evaluate * econtext: evaluation context information * * Outputs: @@ -1697,12 +1707,13 @@ ExecEvalFieldSelect(FieldSelect *fselect, * ---------------------------------------------------------------- */ Datum -ExecEvalExpr(Node *expression, +ExecEvalExpr(ExprState *expression, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Datum retDatum; + Expr *expr; /* Set default values for result flags: non-null, not a set result */ *isNull = false; @@ -1720,111 +1731,112 @@ ExecEvalExpr(Node *expression, * here we dispatch the work to the appropriate type of function given * the type of our expression. */ - switch (nodeTag(expression)) + expr = expression->expr; + switch (nodeTag(expr)) { case T_Var: - retDatum = ExecEvalVar((Var *) expression, econtext, isNull); + retDatum = ExecEvalVar((Var *) expr, econtext, isNull); break; case T_Const: { - Const *con = (Const *) expression; + Const *con = (Const *) expr; retDatum = con->constvalue; *isNull = con->constisnull; break; } case T_Param: - retDatum = ExecEvalParam((Param *) expression, econtext, isNull); + retDatum = ExecEvalParam((Param *) expr, econtext, isNull); break; case T_Aggref: - retDatum = ExecEvalAggref((Aggref *) expression, econtext, isNull); + retDatum = ExecEvalAggref((AggrefExprState *) expression, + econtext, + isNull); break; case T_ArrayRef: - retDatum = ExecEvalArrayRef((ArrayRef *) expression, + retDatum = ExecEvalArrayRef((ArrayRefExprState *) expression, econtext, isNull, isDone); break; case T_FuncExpr: - retDatum = ExecEvalFunc((FuncExpr *) expression, econtext, + retDatum = ExecEvalFunc((FuncExprState *) expression, econtext, isNull, isDone); break; case T_OpExpr: - retDatum = ExecEvalOper((OpExpr *) expression, econtext, + retDatum = ExecEvalOper((FuncExprState *) expression, econtext, isNull, isDone); break; case T_DistinctExpr: - retDatum = ExecEvalDistinct((DistinctExpr *) expression, econtext, + retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext, isNull, isDone); break; case T_BoolExpr: { - BoolExpr *expr = (BoolExpr *) expression; + BoolExprState *state = (BoolExprState *) expression; - switch (expr->boolop) + switch (((BoolExpr *) expr)->boolop) { case AND_EXPR: - retDatum = ExecEvalAnd(expr, econtext, isNull); + retDatum = ExecEvalAnd(state, econtext, isNull); break; case OR_EXPR: - retDatum = ExecEvalOr(expr, econtext, isNull); + retDatum = ExecEvalOr(state, econtext, isNull); break; case NOT_EXPR: - retDatum = ExecEvalNot(expr, econtext, isNull); + retDatum = ExecEvalNot(state, econtext, isNull); break; default: elog(ERROR, "ExecEvalExpr: unknown boolop %d", - expr->boolop); + ((BoolExpr *) expr)->boolop); retDatum = 0; /* keep compiler quiet */ break; } break; } case T_SubPlanExpr: - /* XXX temporary hack to find exec state node */ - retDatum = ExecSubPlan(((SubPlanExpr *) expression)->pstate, - ((SubPlanExpr *) expression)->args, + retDatum = ExecSubPlan((SubPlanExprState *) expression, econtext, isNull); break; case T_FieldSelect: - retDatum = ExecEvalFieldSelect((FieldSelect *) expression, + retDatum = ExecEvalFieldSelect((GenericExprState *) expression, econtext, isNull, isDone); break; case T_RelabelType: - retDatum = ExecEvalExpr((Node *) ((RelabelType *) expression)->arg, + retDatum = ExecEvalExpr(((GenericExprState *) expression)->arg, econtext, isNull, isDone); break; case T_CaseExpr: - retDatum = ExecEvalCase((CaseExpr *) expression, + retDatum = ExecEvalCase((CaseExprState *) expression, econtext, isNull, isDone); break; case T_NullTest: - retDatum = ExecEvalNullTest((NullTest *) expression, + retDatum = ExecEvalNullTest((GenericExprState *) expression, econtext, isNull, isDone); break; case T_BooleanTest: - retDatum = ExecEvalBooleanTest((BooleanTest *) expression, + retDatum = ExecEvalBooleanTest((GenericExprState *) expression, econtext, isNull, isDone); break; case T_ConstraintTest: - retDatum = ExecEvalConstraintTest((ConstraintTest *) expression, + retDatum = ExecEvalConstraintTest((ConstraintTestState *) expression, econtext, isNull, isDone); break; case T_ConstraintTestValue: - retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expression, + retDatum = ExecEvalConstraintTestValue((ConstraintTestValue *) expr, econtext, isNull); break; @@ -1843,7 +1855,7 @@ ExecEvalExpr(Node *expression, * Same as above, but get into the right allocation context explicitly. */ Datum -ExecEvalExprSwitchContext(Node *expression, +ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) @@ -1861,166 +1873,301 @@ ExecEvalExprSwitchContext(Node *expression, /* * ExecInitExpr: prepare an expression tree for execution * + * This function builds and returns an ExprState tree paralleling the given + * Expr node tree. The ExprState tree can then be handed to ExecEvalExpr + * for execution. Because the Expr tree itself is read-only as far as + * ExecInitExpr and ExecEvalExpr are concerned, several different executions + * of the same plan tree can occur concurrently. + * + * This must be called in a memory context that will last as long as repeated + * executions of the expression are needed. Typically the context will be + * the same as the per-query context of the associated ExprContext. + * + * Any Aggref and SubplanExpr nodes found in the tree are added to the lists + * of such nodes held by the parent PlanState. Otherwise, we do very little + * initialization here other than building the state-node tree. Any nontrivial + * work associated with initializing runtime info for a node should happen + * during the first actual evaluation of that node. (This policy lets us + * avoid work if the node is never actually evaluated.) + * + * Note: there is no ExecEndExpr function; we assume that any resource + * cleanup needed will be handled by just releasing the memory context + * in which the state tree is built. Functions that require additional + * cleanup work can register a shutdown callback in the ExprContext. + * * 'node' is the root of the expression tree to examine * 'parent' is the PlanState node that owns the expression, * or NULL if we are preparing an expression that is not associated - * with a plan. (If so, it can't have Aggrefs or SubPlans.) - * - * Soon this will generate an expression state tree paralleling the given - * expression tree. Right now, it just searches the expression tree for - * Aggref and SubPlanExpr nodes. + * with a plan. (If so, it can't have aggs or subplans.) */ -Node * -ExecInitExpr(Node *node, PlanState *parent) +ExprState * +ExecInitExpr(Expr *node, PlanState *parent) { - List *temp; + ExprState *state; if (node == NULL) return NULL; switch (nodeTag(node)) { case T_Var: - break; case T_Const: - break; case T_Param: + case T_ConstraintTestValue: + /* No special setup needed for these node types */ + state = (ExprState *) makeNode(ExprState); break; case T_Aggref: - if (parent && IsA(parent, AggState)) { - AggState *aggstate = (AggState *) parent; - int naggs; + Aggref *aggref = (Aggref *) node; + AggrefExprState *astate = makeNode(AggrefExprState); - aggstate->aggs = lcons(node, aggstate->aggs); - naggs = ++aggstate->numaggs; + if (parent && IsA(parent, AggState)) + { + AggState *aggstate = (AggState *) parent; + int naggs; - ExecInitExpr((Node *) ((Aggref *) node)->target, parent); + aggstate->aggs = lcons(astate, aggstate->aggs); + naggs = ++aggstate->numaggs; - /* - * Complain if the aggregate's argument contains any - * aggregates; nested agg functions are semantically - * nonsensical. (This probably was caught earlier, - * but we defend against it here anyway.) - */ - if (naggs != aggstate->numaggs) - elog(ERROR, "Aggregate function calls may not be nested"); + astate->target = ExecInitExpr(aggref->target, parent); + + /* + * Complain if the aggregate's argument contains any + * aggregates; nested agg functions are semantically + * nonsensical. (This probably was caught earlier, + * but we defend against it here anyway.) + */ + if (naggs != aggstate->numaggs) + elog(ERROR, "Aggregate function calls may not be nested"); + } + else + elog(ERROR, "ExecInitExpr: Aggref not expected here"); + state = (ExprState *) astate; } - else - elog(ERROR, "ExecInitExpr: Aggref not expected here"); break; case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; - - ExecInitExpr((Node *) aref->refupperindexpr, parent); - ExecInitExpr((Node *) aref->reflowerindexpr, parent); - ExecInitExpr((Node *) aref->refexpr, parent); - ExecInitExpr((Node *) aref->refassgnexpr, parent); + ArrayRefExprState *astate = makeNode(ArrayRefExprState); + + astate->refupperindexpr = (List *) + ExecInitExpr((Expr *) aref->refupperindexpr, parent); + astate->reflowerindexpr = (List *) + ExecInitExpr((Expr *) aref->reflowerindexpr, parent); + astate->refexpr = ExecInitExpr(aref->refexpr, parent); + astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr, + parent); + state = (ExprState *) astate; } break; case T_FuncExpr: { FuncExpr *funcexpr = (FuncExpr *) node; + FuncExprState *fstate = makeNode(FuncExprState); - ExecInitExpr((Node *) funcexpr->args, parent); + fstate->args = (List *) + ExecInitExpr((Expr *) funcexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState *) fstate; } break; case T_OpExpr: { OpExpr *opexpr = (OpExpr *) node; + FuncExprState *fstate = makeNode(FuncExprState); - ExecInitExpr((Node *) opexpr->args, parent); + fstate->args = (List *) + ExecInitExpr((Expr *) opexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState *) fstate; } break; case T_DistinctExpr: { DistinctExpr *distinctexpr = (DistinctExpr *) node; + FuncExprState *fstate = makeNode(FuncExprState); - ExecInitExpr((Node *) distinctexpr->args, parent); + fstate->args = (List *) + ExecInitExpr((Expr *) distinctexpr->args, parent); + fstate->func.fn_oid = InvalidOid; /* not initialized */ + state = (ExprState *) fstate; } break; case T_BoolExpr: { BoolExpr *boolexpr = (BoolExpr *) node; + BoolExprState *bstate = makeNode(BoolExprState); - ExecInitExpr((Node *) boolexpr->args, parent); + bstate->args = (List *) + ExecInitExpr((Expr *) boolexpr->args, parent); + state = (ExprState *) bstate; } break; case T_SubPlanExpr: { + /* Keep this in sync with ExecInitExprInitPlan, below */ SubPlanExpr *subplanexpr = (SubPlanExpr *) node; SubLink *sublink = subplanexpr->sublink; + SubPlanExprState *sstate = makeNode(SubPlanExprState); Assert(IsA(sublink, SubLink)); if (!parent) elog(ERROR, "ExecInitExpr: SubPlanExpr not expected here"); /* - * Here we just add the SubPlanExpr nodes to - * parent->subPlan. Later they will be expanded - * to SubPlanState nodes. + * Here we just add the SubPlanExprState nodes to + * parent->subPlan. The subplans will be initialized later. */ - parent->subPlan = lcons(subplanexpr, parent->subPlan); + parent->subPlan = lcons(sstate, parent->subPlan); + sstate->planstate = NULL; + + sstate->args = (List *) + ExecInitExpr((Expr *) subplanexpr->args, parent); - /* Must recurse into oper list too */ if (sublink->lefthand) elog(ERROR, "ExecInitExpr: sublink has not been transformed"); - ExecInitExpr((Node *) sublink->oper, parent); + sstate->oper = (List *) + ExecInitExpr((Expr *) sublink->oper, parent); - ExecInitExpr((Node *) subplanexpr->args, parent); + state = (ExprState *) sstate; } break; case T_FieldSelect: - ExecInitExpr((Node *) ((FieldSelect *) node)->arg, parent); + { + FieldSelect *fselect = (FieldSelect *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->arg = ExecInitExpr(fselect->arg, parent); + state = (ExprState *) gstate; + } break; case T_RelabelType: - ExecInitExpr((Node *) ((RelabelType *) node)->arg, parent); + { + RelabelType *relabel = (RelabelType *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->arg = ExecInitExpr(relabel->arg, parent); + state = (ExprState *) gstate; + } break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; + CaseExprState *cstate = makeNode(CaseExprState); + List *outlist = NIL; + List *inlist; - foreach(temp, caseexpr->args) + foreach(inlist, caseexpr->args) { - CaseWhen *when = (CaseWhen *) lfirst(temp); + CaseWhen *when = (CaseWhen *) lfirst(inlist); + CaseWhenState *wstate = makeNode(CaseWhenState); Assert(IsA(when, CaseWhen)); - ExecInitExpr((Node *) when->expr, parent); - ExecInitExpr((Node *) when->result, parent); + wstate->xprstate.expr = (Expr *) when; + wstate->expr = ExecInitExpr(when->expr, parent); + wstate->result = ExecInitExpr(when->result, parent); + outlist = lappend(outlist, wstate); } - /* caseexpr->arg should be null, but we'll check it anyway */ - ExecInitExpr((Node *) caseexpr->arg, parent); - ExecInitExpr((Node *) caseexpr->defresult, parent); + cstate->args = outlist; + /* caseexpr->arg should be null by now */ + Assert(caseexpr->arg == NULL); + cstate->defresult = ExecInitExpr(caseexpr->defresult, parent); + state = (ExprState *) cstate; } break; case T_NullTest: - ExecInitExpr((Node *) ((NullTest *) node)->arg, parent); + { + NullTest *ntest = (NullTest *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->arg = ExecInitExpr(ntest->arg, parent); + state = (ExprState *) gstate; + } break; case T_BooleanTest: - ExecInitExpr((Node *) ((BooleanTest *) node)->arg, parent); + { + BooleanTest *btest = (BooleanTest *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->arg = ExecInitExpr(btest->arg, parent); + state = (ExprState *) gstate; + } break; case T_ConstraintTest: - ExecInitExpr((Node *) ((ConstraintTest *) node)->arg, parent); - ExecInitExpr((Node *) ((ConstraintTest *) node)->check_expr, parent); - break; - case T_ConstraintTestValue: + { + ConstraintTest *ctest = (ConstraintTest *) node; + ConstraintTestState *cstate = makeNode(ConstraintTestState); + + cstate->arg = ExecInitExpr(ctest->arg, parent); + cstate->check_expr = ExecInitExpr(ctest->check_expr, parent); + state = (ExprState *) cstate; + } break; case T_TargetEntry: - ExecInitExpr((Node *) ((TargetEntry *) node)->expr, parent); + { + TargetEntry *tle = (TargetEntry *) node; + GenericExprState *gstate = makeNode(GenericExprState); + + gstate->arg = ExecInitExpr(tle->expr, parent); + state = (ExprState *) gstate; + } break; case T_List: - foreach(temp, (List *) node) { - ExecInitExpr((Node *) lfirst(temp), parent); + List *outlist = NIL; + List *inlist; + + foreach(inlist, (List *) node) + { + outlist = lappend(outlist, + ExecInitExpr((Expr *) lfirst(inlist), + parent)); + } + /* Don't fall through to the "common" code below */ + return (ExprState *) outlist; } - break; default: elog(ERROR, "ExecInitExpr: unknown expression type %d", nodeTag(node)); + state = NULL; /* keep compiler quiet */ break; } - return node; + /* Common code for all state-node types */ + state->expr = node; + + return state; +} + +/* + * ExecInitExprInitPlan --- initialize a subplan expr that's being handled + * as an InitPlan. This is identical to ExecInitExpr's handling of a regular + * subplan expr, except we do NOT want to add the node to the parent's + * subplan list. + */ +SubPlanExprState * +ExecInitExprInitPlan(SubPlanExpr *node, PlanState *parent) +{ + SubLink *sublink = node->sublink; + SubPlanExprState *sstate = makeNode(SubPlanExprState); + + Assert(IsA(sublink, SubLink)); + if (!parent) + elog(ERROR, "ExecInitExpr: SubPlanExpr not expected here"); + + /* The subplan's state will be initialized later */ + sstate->planstate = NULL; + + sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent); + + if (sublink->lefthand) + elog(ERROR, "ExecInitExpr: sublink has not been transformed"); + + sstate->oper = (List *) ExecInitExpr((Expr *) sublink->oper, parent); + + sstate->xprstate.expr = (Expr *) node; + + return sstate; } @@ -2094,7 +2241,7 @@ ExecQual(List *qual, ExprContext *econtext, bool resultForNull) foreach(qlist, qual) { - Node *clause = (Node *) lfirst(qlist); + ExprState *clause = (ExprState *) lfirst(qlist); Datum expr_value; bool isNull; @@ -2146,6 +2293,7 @@ ExecCleanTargetListLength(List *targetlist) { TargetEntry *curTle = (TargetEntry *) lfirst(tl); + Assert(IsA(curTle, TargetEntry)); if (!curTle->resdom->resjunk) len++; } @@ -2180,8 +2328,6 @@ ExecTargetList(List *targetlist, char *nulls; ExprDoneCond *itemIsDone; List *tl; - TargetEntry *tle; - AttrNumber resind; HeapTuple newTuple; bool isNull; bool haveDoneSets; @@ -2243,11 +2389,11 @@ ExecTargetList(List *targetlist, foreach(tl, targetlist) { - tle = lfirst(tl); - - resind = tle->resdom->resno - 1; + GenericExprState *gstate = (GenericExprState *) lfirst(tl); + TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; + AttrNumber resind = tle->resdom->resno - 1; - values[resind] = ExecEvalExpr((Node *) tle->expr, + values[resind] = ExecEvalExpr(gstate->arg, econtext, &isNull, &itemIsDone[resind]); @@ -2295,29 +2441,26 @@ ExecTargetList(List *targetlist, */ foreach(tl, targetlist) { - tle = lfirst(tl); + GenericExprState *gstate = (GenericExprState *) lfirst(tl); + TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; + AttrNumber resind = tle->resdom->resno - 1; - if (tle->resdom != NULL) + if (itemIsDone[resind] == ExprEndResult) { - resind = tle->resdom->resno - 1; + values[resind] = ExecEvalExpr(gstate->arg, + econtext, + &isNull, + &itemIsDone[resind]); + nulls[resind] = isNull ? 'n' : ' '; if (itemIsDone[resind] == ExprEndResult) { - values[resind] = ExecEvalExpr((Node *) tle->expr, - econtext, - &isNull, - &itemIsDone[resind]); - nulls[resind] = isNull ? 'n' : ' '; - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty - * set. Guess we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } + /* + * Oh dear, this item is returning an empty + * set. Guess we can't make a tuple after all. + */ + *isDone = ExprEndResult; + break; } } } @@ -2326,24 +2469,23 @@ ExecTargetList(List *targetlist, * 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. + * + * XXX is that still necessary? */ if (*isDone == ExprEndResult) { foreach(tl, targetlist) { - tle = lfirst(tl); + GenericExprState *gstate = (GenericExprState *) lfirst(tl); + TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; + AttrNumber resind = tle->resdom->resno - 1; - if (tle->resdom != NULL) + while (itemIsDone[resind] == ExprMultipleResult) { - resind = tle->resdom->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - (void) ExecEvalExpr((Node *) tle->expr, - econtext, - &isNull, - &itemIsDone[resind]); - } + (void) ExecEvalExpr(gstate->arg, + econtext, + &isNull, + &itemIsDone[resind]); } } |