/*------------------------------------------------------------------------- * * execQual.c * Routines to evaluate qualification and targetlist expressions * * Portions Copyright (c) 1996-2004, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.168 2004/08/29 05:06:42 momjian Exp $ * *------------------------------------------------------------------------- */ /* * INTERFACE ROUTINES * ExecEvalExpr - (now a macro) evaluate an expression, return a datum * ExecEvalExprSwitchContext - same, but switch into eval memory context * ExecQual - return true/false if qualification is satisfied * ExecProject - form a new tuple by projecting the given tuple * * NOTES * The more heavily used ExecEvalExpr routines, such as ExecEvalVar(), * are hotspots. Making these faster will speed up the entire system. * * ExecProject() is used to make tuple projections. Rather then * trying to speed it up, the execution plan should be pre-processed * to facilitate attribute sharing between nodes wherever possible, * instead of doing needless copying. -cim 5/31/91 * * During expression evaluation, we check_stack_depth only in * ExecMakeFunctionResult rather than at every single node. This * is a compromise that trades off precision of the stack limit setting * to gain speed. */ #include "postgres.h" #include "access/heapam.h" #include "catalog/pg_type.h" #include "commands/typecmds.h" #include "executor/execdebug.h" #include "executor/functions.h" #include "executor/nodeSubplan.h" #include "funcapi.h" #include "miscadmin.h" #include "nodes/makefuncs.h" #include "optimizer/planmain.h" #include "parser/parse_expr.h" #include "utils/acl.h" #include "utils/array.h" #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/typcache.h" /* static function decls */ static Datum ExecEvalArrayRef(ArrayRefExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext); static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCaseTestExpr(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalRow(RowExprState *rstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalNullTest(GenericExprState *nstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalBooleanTest(GenericExprState *bstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalFieldStore(FieldStoreState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone); /* ---------------------------------------------------------------- * ExecEvalExpr routines * * Recursively evaluate a targetlist or qualification expression. * * Each of the following routines having the signature * Datum ExecEvalFoo(ExprState *expression, * ExprContext *econtext, * bool *isNull, * ExprDoneCond *isDone); * is responsible for evaluating one type or subtype of ExprState node. * They are normally called via the ExecEvalExpr macro, which makes use of * the function pointer set up when the ExprState node was built by * ExecInitExpr. (In some cases, we change this pointer later to avoid * re-executing one-time overhead.) * * Note: for notational simplicity we declare these functions as taking the * specific type of ExprState that they work on. This requires casting when * assigning the function pointer in ExecInitExpr. Be careful that the * function signature is declared correctly, because the cast suppresses * automatic checking! * * * All these functions share this calling convention: * * Inputs: * expression: the expression state 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 error * will be reported via ereport. 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. * * 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 in these routines * because it'd be a waste of cycles during nested expression evaluation. * ---------------------------------------------------------------- */ /*---------- * ExecEvalArrayRef * * This function takes an ArrayRef and returns the extracted Datum * if it's a simple reference, or the modified array value if it's * an array assignment (i.e., array element or slice insertion). * * NOTE: if we get a NULL result from a subexpression, we return NULL when * it's an array reference, or the unmodified source array when it's an * array assignment. This may seem peculiar, but if we return NULL (as was * done in versions up through 7.0) then an assignment like * UPDATE table SET arrayfield[4] = NULL * will result in setting the whole array to NULL, which is certainly not * very desirable. By returning the source array we make the assignment * into a no-op, instead. (Eventually we need to redesign arrays so that * individual elements can be NULL, but for now, let's try to protect users * from shooting themselves in the foot.) * * NOTE: we deliberately refrain from applying DatumGetArrayTypeP() here, * even though that might seem natural, because this code needs to support * both varlena arrays and fixed-length array types. DatumGetArrayTypeP() * only works for the varlena kind. The routines we call in arrayfuncs.c * have to know the difference (that's what they need refattrlength for). *---------- */ static Datum 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); bool eisnull; ListCell *l; int i = 0, j = 0; IntArray upper, lower; int *lIndex; array_source = (ArrayType *) DatumGetPointer(ExecEvalExpr(astate->refexpr, econtext, isNull, isDone)); /* * If refexpr yields NULL, and it's a fetch, then result is NULL. In * the assignment case, we'll cons up something below. */ if (*isNull) { if (isDone && *isDone == ExprEndResult) return (Datum) NULL; /* end of set result */ if (!isAssignment) return (Datum) NULL; } foreach(l, astate->refupperindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); if (i >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i, MAXDIM))); upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); /* If any index expr yields NULL, result is NULL or source array */ if (eisnull) { if (!isAssignment) { *isNull = true; return (Datum) NULL; } return PointerGetDatum(array_source); } } if (astate->reflowerindexpr != NIL) { foreach(l, astate->reflowerindexpr) { ExprState *eltstate = (ExprState *) lfirst(l); if (j >= MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)", i, MAXDIM))); lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate, econtext, &eisnull, NULL)); /* * If any index expr yields NULL, result is NULL or source * array */ if (eisnull) { if (!isAssignment) { *isNull = true; return (Datum) NULL; } return PointerGetDatum(array_source); } } /* this can't happen unless parser messed up */ if (i != j) elog(ERROR, "upper and lower index lists are not same length"); lIndex = lower.indx; } else lIndex = NULL; if (isAssignment) { Datum sourceData; /* * Evaluate the value to be assigned into the array. * * XXX At some point we'll need to look into making the old value of * the array element available via CaseTestExpr, as is done by * ExecEvalFieldStore. This is not needed now but will be needed * to support arrays of composite types; in an assignment to a * field of an array member, the parser would generate a * FieldStore that expects to fetch its input tuple via * CaseTestExpr. */ sourceData = ExecEvalExpr(astate->refassgnexpr, econtext, &eisnull, NULL); /* * For now, can't cope with inserting NULL into an array, so make * it a no-op per discussion above... */ if (eisnull) return PointerGetDatum(array_source); /* * For an assignment, if all the subscripts and the input * expression are non-null but the original array is null, then * substitute an empty (zero-dimensional) array and proceed with * the assignment. This only works for varlena arrays, though; for * fixed-length array types we punt and return the null input * array. */ if (*isNull) { if (astate->refattrlength > 0) /* fixed-length array? */ return PointerGetDatum(array_source); array_source = construct_md_array(NULL, 0, NULL, NULL, arrayRef->refelemtype, astate->refelemlength, astate->refelembyval, astate->refelemalign); *isNull = false; } if (lIndex == NULL) resultArray = array_set(array_source, i, upper.indx, sourceData, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); else resultArray = array_set_slice(array_source, i, upper.indx, lower.indx, (ArrayType *) DatumGetPointer(sourceData), astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); return PointerGetDatum(resultArray); } if (lIndex == NULL) return array_ref(array_source, i, upper.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); else { resultArray = array_get_slice(array_source, i, upper.indx, lower.indx, astate->refattrlength, astate->refelemlength, astate->refelembyval, astate->refelemalign, isNull); return PointerGetDatum(resultArray); } } /* ---------------------------------------------------------------- * ExecEvalAggref * * Returns a Datum whose value is the value of the precomputed * aggregate found in the given expression context. * ---------------------------------------------------------------- */ static Datum ExecEvalAggref(AggrefExprState *aggref, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { if (isDone) *isDone = ExprSingleResult; if (econtext->ecxt_aggvalues == NULL) /* safety check */ elog(ERROR, "no aggregates in this expression context"); *isNull = econtext->ecxt_aggnulls[aggref->aggno]; return econtext->ecxt_aggvalues[aggref->aggno]; } /* ---------------------------------------------------------------- * ExecEvalVar * * Returns a Datum whose value is the value of a range * variable with respect to given expression context. * ---------------------------------------------------------------- */ static Datum ExecEvalVar(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Var *variable = (Var *) exprstate->expr; Datum result; TupleTableSlot *slot; AttrNumber attnum; HeapTuple heapTuple; TupleDesc tuple_type; if (isDone) *isDone = ExprSingleResult; /* * Get the slot and attribute number we want * * The asserts check that references to system attributes only appear at * the level of a relation scan; at higher levels, system attributes * must be treated as ordinary variables (since we no longer have * access to the original tuple). */ attnum = variable->varattno; switch (variable->varno) { case INNER: /* get the tuple from the inner node */ slot = econtext->ecxt_innertuple; Assert(attnum > 0); break; case OUTER: /* get the tuple from the outer node */ slot = econtext->ecxt_outertuple; Assert(attnum > 0); break; default: /* get the tuple from the relation being * scanned */ slot = econtext->ecxt_scantuple; break; } /* * extract tuple information from the slot */ heapTuple = slot->val; tuple_type = slot->ttc_tupleDescriptor; /* * Some checks that are only applied for user attribute numbers (bogus * system attnums will be caught inside heap_getattr). */ if (attnum > 0) { /* * This assert checks that the attnum is valid. */ Assert(attnum <= tuple_type->natts && tuple_type->attrs[attnum - 1] != NULL); /* * If the attribute's column has been dropped, we force a NULL * result. This case should not happen in normal use, but it could * happen if we are executing a plan cached before the column was * dropped. */ if (tuple_type->attrs[attnum - 1]->attisdropped) { *isNull = true; return (Datum) 0; } /* * This assert checks that the datatype the plan expects to get * (as told by our "variable" argument) is in fact the datatype of * the attribute being fetched (as seen in the current context, * identified by our "econtext" argument). Otherwise crashes are * likely. * * Note that we can't check dropped columns, since their atttypid has * been zeroed. */ Assert(variable->vartype == tuple_type->attrs[attnum - 1]->atttypid); } result = heap_getattr(heapTuple, /* tuple containing attribute */ attnum, /* attribute number of desired * attribute */ tuple_type, /* tuple descriptor of tuple */ isNull); /* return: is attribute null? */ return result; } /* ---------------------------------------------------------------- * ExecEvalConst * * Returns the value of a constant. * * Note that for pass-by-ref datatypes, we return a pointer to the * actual constant node. This is one of the reasons why functions * must treat their input arguments as read-only. * ---------------------------------------------------------------- */ static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Const *con = (Const *) exprstate->expr; if (isDone) *isDone = ExprSingleResult; *isNull = con->constisnull; return con->constvalue; } /* ---------------------------------------------------------------- * ExecEvalParam * * Returns the value of a parameter. A param node contains * something like ($.name) and the expression context contains * the current parameter bindings (name = "sam") (age = 34)... * so our job is to find and return the appropriate datum ("sam"). * * Q: if we have a parameter ($.foo) without a binding, i.e. * there is no (foo = xxx) in the parameter list info, * is this a fatal error or should this be a "not available" * (in which case we could return NULL)? -cim 10/13/89 * ---------------------------------------------------------------- */ static Datum ExecEvalParam(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Param *expression = (Param *) exprstate->expr; int thisParamKind = expression->paramkind; AttrNumber thisParamId = expression->paramid; if (isDone) *isDone = ExprSingleResult; if (thisParamKind == PARAM_EXEC) { /* * PARAM_EXEC params (internal executor parameters) are stored in * the ecxt_param_exec_vals array, and can be accessed by array * index. */ ParamExecData *prm; prm = &(econtext->ecxt_param_exec_vals[thisParamId]); if (prm->execPlan != NULL) { /* Parameter not evaluated yet, so go do it */ ExecSetParamPlan(prm->execPlan, econtext); /* ExecSetParamPlan should have processed this param... */ Assert(prm->execPlan == NULL); } *isNull = prm->isnull; return prm->value; } else { /* * All other parameter types must be sought in * ecxt_param_list_info. */ ParamListInfo paramInfo; paramInfo = lookupParam(econtext->ecxt_param_list_info, thisParamKind, expression->paramname, thisParamId, false); Assert(paramInfo->ptype == expression->paramtype); *isNull = paramInfo->isnull; return paramInfo->value; } } /* ---------------------------------------------------------------- * ExecEvalOper / ExecEvalFunc support routines * ---------------------------------------------------------------- */ /* * GetAttributeByName * GetAttributeByNum * * These functions return the value of the requested attribute * out of the given tuple Datum. * C functions which take a tuple as an argument are expected * to use these. Ex: overpaid(EMP) might call GetAttributeByNum(). * Note: these are actually rather slow because they do a typcache * lookup on each call. */ Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno, bool *isNull) { Datum result; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; if (!AttributeNumberIsValid(attrno)) elog(ERROR, "invalid attribute number %d", attrno); if (isNull == NULL) elog(ERROR, "a NULL isNull pointer was passed"); if (tuple == NULL) { /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum) 0; } tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set * all the fields in the struct just in case user tries to inspect * system columns. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = tuple; result = heap_getattr(&tmptup, attrno, tupDesc, isNull); return result; } Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull) { AttrNumber attrno; Datum result; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; int i; if (attname == NULL) elog(ERROR, "invalid attribute name"); if (isNull == NULL) elog(ERROR, "a NULL isNull pointer was passed"); if (tuple == NULL) { /* Kinda bogus but compatible with old behavior... */ *isNull = true; return (Datum) 0; } tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); attrno = InvalidAttrNumber; for (i = 0; i < tupDesc->natts; i++) { if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0) { attrno = tupDesc->attrs[i]->attnum; break; } } if (attrno == InvalidAttrNumber) elog(ERROR, "attribute \"%s\" does not exist", attname); /* * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set * all the fields in the struct just in case user tries to inspect * system columns. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = tuple; result = heap_getattr(&tmptup, attrno, tupDesc, isNull); return result; } /* * 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, ACL_KIND_PROC, get_func_name(foid)); /* Safety check (should never fail, as parser should check sooner) */ if (list_length(fcache->args) > FUNC_MAX_ARGS) elog(ERROR, "too many arguments"); /* Set up the primary fmgr lookup information */ fmgr_info_cxt(foid, &(fcache->func), fcacheCxt); /* Initialize additional info */ fcache->setArgsValid = false; fcache->shutdown_reg = false; fcache->func.fn_expr = (Node *) fcache->xprstate.expr; } /* * callback function in case a FuncExpr returning a set needs to be shut down * before it has been run to completion */ static void ShutdownFuncExpr(Datum arg) { FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg); /* Clear any active set-argument state */ fcache->setArgsValid = false; /* execUtils will deregister the callback... */ fcache->shutdown_reg = false; } /* * Evaluate arguments for a function. */ static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo, List *argList, ExprContext *econtext) { ExprDoneCond argIsDone; int i; ListCell *arg; argIsDone = ExprSingleResult; /* default assumption */ i = 0; foreach(arg, argList) { ExprState *argstate = (ExprState *) lfirst(arg); ExprDoneCond thisArgIsDone; fcinfo->arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo->argnull[i], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) { /* * 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) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("functions and operators can take at most one set argument"))); argIsDone = thisArgIsDone; } i++; } fcinfo->nargs = i; return argIsDone; } /* * ExecMakeFunctionResult * * Evaluate the arguments to a function and then the function itself. */ Datum ExecMakeFunctionResult(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { List *arguments = fcache->args; Datum result; FunctionCallInfoData fcinfo; ReturnSetInfo rsinfo; /* for functions returning sets */ ExprDoneCond argDone; bool hasSetArg; int i; /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); /* * arguments is a list of expressions to evaluate before passing to * 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 fcinfo. */ if (!fcache->setArgsValid) { /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); if (argDone == ExprEndResult) { /* input is an empty set, so return an empty set. */ *isNull = true; if (isDone) *isDone = ExprEndResult; else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); return (Datum) 0; } hasSetArg = (argDone != ExprSingleResult); } else { /* Copy callinfo from previous evaluation */ memcpy(&fcinfo, &fcache->setArgs, sizeof(fcinfo)); hasSetArg = fcache->setHasSetArg; /* Reset flag (we may set it again below) */ fcache->setArgsValid = false; } /* * If function returns set, prepare a resultinfo node for * communication */ if (fcache->func.fn_retset) { fcinfo.resultinfo = (Node *) &rsinfo; rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = NULL; rsinfo.allowedModes = (int) SFRM_ValuePerCall; rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; } /* * now return the value gotten by calling the function manager, * passing the function the evaluated parameter values. */ if (fcache->func.fn_retset || hasSetArg) { /* * We need to return a set result. Complain if caller not ready * to accept one. */ if (isDone == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("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 (;;) { /* * If function is strict, and there are any NULL arguments, * skip calling the function (at least for this set of args). */ bool callit = true; if (fcache->func.fn_strict) { for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { callit = false; break; } } } if (callit) { fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; *isDone = rsinfo.isDone; } else { result = (Datum) 0; *isNull = true; *isDone = ExprEndResult; } if (*isDone != ExprEndResult) { /* * Got a result from current argument. If function itself * returns set, save the current argument values to re-use * on the next call. */ if (fcache->func.fn_retset) { memcpy(&fcache->setArgs, &fcinfo, sizeof(fcinfo)); fcache->setHasSetArg = hasSetArg; fcache->setArgsValid = true; /* Register cleanup callback if we didn't already */ if (!fcache->shutdown_reg) { RegisterExprContextCallback(econtext, ShutdownFuncExpr, PointerGetDatum(fcache)); fcache->shutdown_reg = true; } } /* * Make sure we say we are returning a set, even if the * function itself doesn't return sets. */ *isDone = ExprMultipleResult; break; } /* Else, done with this argument */ if (!hasSetArg) break; /* input not a set, so done */ /* Re-eval args to get the next element of the input set */ argDone = ExecEvalFuncArgs(&fcinfo, arguments, econtext); if (argDone != ExprMultipleResult) { /* End of argument set, so we're done. */ *isNull = true; *isDone = ExprEndResult; result = (Datum) 0; break; } /* * If we reach here, loop around to run the function on the * new argument. */ } } else { /* * Non-set case: much easier. * * We change the ExprState function pointer to use the simpler * ExecMakeFunctionResultNoSets on subsequent calls. This amounts * to assuming that no argument can return a set if it didn't do * so the first time. */ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets; if (isDone) *isDone = ExprSingleResult; /* * If function is strict, and there are any NULL arguments, skip * calling the function and return NULL. */ if (fcache->func.fn_strict) { for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { *isNull = true; return (Datum) 0; } } } fcinfo.isnull = false; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; } return result; } /* * ExecMakeFunctionResultNoSets * * Simplified version of ExecMakeFunctionResult that can only handle * non-set cases. Hand-tuned for speed. */ static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { ListCell *arg; Datum result; FunctionCallInfoData fcinfo; int i; /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); if (isDone) *isDone = ExprSingleResult; MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); /* inlined, simplified version of ExecEvalFuncArgs */ i = 0; foreach(arg, fcache->args) { ExprState *argstate = (ExprState *) lfirst(arg); ExprDoneCond thisArgIsDone; fcinfo.arg[i] = ExecEvalExpr(argstate, econtext, &fcinfo.argnull[i], &thisArgIsDone); if (thisArgIsDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); i++; } fcinfo.nargs = i; /* * If function is strict, and there are any NULL arguments, skip * calling the function and return NULL. */ if (fcache->func.fn_strict) { while (--i >= 0) { if (fcinfo.argnull[i]) { *isNull = true; return (Datum) 0; } } } /* fcinfo.isnull = false; */ /* handled by MemSet */ result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; return result; } /* * ExecMakeTableFunctionResult * * Evaluate a table function, producing a materialized result in a Tuplestore * object. (If function returns an empty set, we just return NULL instead.) */ Tuplestorestate * ExecMakeTableFunctionResult(ExprState *funcexpr, ExprContext *econtext, TupleDesc expectedDesc, TupleDesc *returnDesc) { Tuplestorestate *tupstore = NULL; TupleDesc tupdesc = NULL; Oid funcrettype; bool returnsTuple; FunctionCallInfoData fcinfo; ReturnSetInfo rsinfo; HeapTupleData tmptup; MemoryContext callerContext; MemoryContext oldcontext; bool direct_function_call; bool first_time = true; /* * 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 or inlining. So if we see any other kind of * expression node, execute it via the general ExecEvalExpr() code; * the only difference is that we don't get a chance to pass a special * ReturnSetInfo to any functions buried in the expression. */ if (funcexpr && IsA(funcexpr, FuncExprState) && IsA(funcexpr->expr, FuncExpr)) { FuncExprState *fcache = (FuncExprState *) funcexpr; ExprDoneCond argDone; /* * This path is similar to ExecMakeFunctionResult. */ direct_function_call = true; /* * Initialize function cache if first time through */ if (fcache->func.fn_oid == InvalidOid) { FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory); } /* * Evaluate the function's argument list. * * Note: ideally, we'd do this in the per-tuple context, but then the * argument values would disappear when we reset the context in * the inner loop. So do it in caller context. Perhaps we should * make a separate context just to hold the evaluated arguments? */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); argDone = ExecEvalFuncArgs(&fcinfo, fcache->args, econtext); /* We don't allow sets in the arguments of the table function */ if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); /* * If function is strict, and there are any NULL arguments, skip * calling the function and return NULL (actually an empty set). */ if (fcache->func.fn_strict) { int i; for (i = 0; i < fcinfo.nargs; i++) { if (fcinfo.argnull[i]) { *returnDesc = NULL; return NULL; } } } } else { /* Treat funcexpr as a generic expression */ direct_function_call = false; } funcrettype = exprType((Node *) funcexpr->expr); returnsTuple = (funcrettype == RECORDOID || get_typtype(funcrettype) == 'c'); /* * Prepare a resultinfo node for communication. We always do this * even if not expecting a set result, so that we can pass * expectedDesc. In the generic-expression case, the expression * doesn't actually get to see the resultinfo, but set it up anyway * because we use some of the fields as our own state variables. */ fcinfo.resultinfo = (Node *) &rsinfo; rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = expectedDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize); rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * Switch to short-lived context for calling the function or * expression. */ callerContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Loop to handle the ValuePerCall protocol (which is also the same * behavior needed in the generic ExecEvalExpr path). */ for (;;) { Datum result; HeapTuple tuple; /* * reset per-tuple memory context before each call of the function * or expression. This cleans up any local memory the function may * leak when called. */ ResetExprContext(econtext); /* Call the function or expression one time */ if (direct_function_call) { fcinfo.isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(&fcinfo); } else { result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull, &rsinfo.isDone); } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { /* * Check for end of result set. * * Note: if function returns an empty set, we don't build a * tupdesc or tuplestore (since we can't get a tupdesc in the * function-returning-tuple case) */ if (rsinfo.isDone == ExprEndResult) break; /* * Can't do anything useful with NULL rowtype values. * Currently we raise an error, but another alternative is to * just ignore the result and "continue" to get another row. */ if (returnsTuple && fcinfo.isnull) ereport(ERROR, (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), errmsg("function returning row cannot return null value"))); /* * If first time through, build tupdesc and tuplestore for * result */ if (first_time) { oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); if (returnsTuple) { /* * Use the type info embedded in the rowtype Datum to * look up the needed tupdesc. Make a copy for the * query. */ HeapTupleHeader td; td = DatumGetHeapTupleHeader(result); tupdesc = lookup_rowtype_tupdesc(HeapTupleHeaderGetTypeId(td), HeapTupleHeaderGetTypMod(td)); tupdesc = CreateTupleDescCopy(tupdesc); } else { /* * Scalar type, so make a single-column descriptor */ tupdesc = CreateTemplateTupleDesc(1, false); TupleDescInitEntry(tupdesc, (AttrNumber) 1, "column", funcrettype, -1, 0); } tupstore = tuplestore_begin_heap(true, false, work_mem); MemoryContextSwitchTo(oldcontext); rsinfo.setResult = tupstore; rsinfo.setDesc = tupdesc; } /* * Store current resultset item. */ if (returnsTuple) { HeapTupleHeader td; td = DatumGetHeapTupleHeader(result); /* * tuplestore_puttuple needs a HeapTuple not a bare * HeapTupleHeader, but it doesn't need all the fields. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(td); tmptup.t_data = td; tuple = &tmptup; } else { char nullflag; nullflag = fcinfo.isnull ? 'n' : ' '; tuple = heap_formtuple(tupdesc, &result, &nullflag); } oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tuplestore_puttuple(tupstore, tuple); MemoryContextSwitchTo(oldcontext); /* * Are we done? */ if (rsinfo.isDone != ExprMultipleResult) break; } else if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (!first_time || rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); /* Done evaluating the set result */ break; } else ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int) rsinfo.returnMode))); first_time = false; } MemoryContextSwitchTo(callerContext); /* The returned pointers are those in rsinfo */ *returnDesc = rsinfo.setDesc; return rsinfo.setResult; } /* ---------------------------------------------------------------- * ExecEvalFunc * ExecEvalOper * * Evaluate the functional result of a list of arguments by calling the * function manager. * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * ExecEvalFunc * ---------------------------------------------------------------- */ static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { /* This is called only the first time through */ FuncExpr *func = (FuncExpr *) fcache->xprstate.expr; /* Initialize function lookup info */ init_fcache(func->funcid, fcache, econtext->ecxt_per_query_memory); /* Go directly to ExecMakeFunctionResult on subsequent uses */ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } /* ---------------------------------------------------------------- * ExecEvalOper * ---------------------------------------------------------------- */ static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { /* This is called only the first time through */ OpExpr *op = (OpExpr *) fcache->xprstate.expr; /* Initialize function lookup info */ init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory); /* Go directly to ExecMakeFunctionResult on subsequent uses */ fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResult; return ExecMakeFunctionResult(fcache, econtext, isNull, isDone); } /* ---------------------------------------------------------------- * ExecEvalDistinct * * IS DISTINCT FROM must evaluate arguments to determine whether * they are NULL; if either is NULL then the result is already * known. If neither is NULL, then proceed to evaluate the * function. Note that this is *always* derived from the equals * operator, but since we need special processing of the arguments * we can not simply reuse ExecEvalOper() or ExecEvalFunc(). * ---------------------------------------------------------------- */ static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Datum result; FunctionCallInfoData fcinfo; ExprDoneCond argDone; List *argList; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ 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); } /* * extract info from fcache */ argList = fcache->args; /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(fcache->func); argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("IS DISTINCT FROM does not support set arguments"))); Assert(fcinfo.nargs == 2); if (fcinfo.argnull[0] && fcinfo.argnull[1]) { /* Both NULL? Then is not distinct... */ result = BoolGetDatum(FALSE); } else if (fcinfo.argnull[0] || fcinfo.argnull[1]) { /* Only one is NULL? Then is distinct... */ result = BoolGetDatum(TRUE); } else { fcinfo.isnull = false; result = FunctionCallInvoke(&fcinfo); *isNull = fcinfo.isnull; /* Must invert result of "=" */ result = BoolGetDatum(!DatumGetBool(result)); } return result; } /* * ExecEvalScalarArrayOp * * Evaluate "scalar op ANY/ALL (array)". The operator always yields boolean, * and we combine the results across all array elements using OR and AND * (for ANY and ALL respectively). Of course we short-circuit as soon as * the result is known. */ static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) sstate->fxprstate.xprstate.expr; bool useOr = opexpr->useOr; ArrayType *arr; int nitems; Datum result; bool resultnull; FunctionCallInfoData fcinfo; ExprDoneCond argDone; int i; int16 typlen; bool typbyval; char typalign; char *s; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ if (sstate->fxprstate.func.fn_oid == InvalidOid) { init_fcache(opexpr->opfuncid, &sstate->fxprstate, econtext->ecxt_per_query_memory); Assert(!sstate->fxprstate.func.fn_retset); } /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(sstate->fxprstate.func); argDone = ExecEvalFuncArgs(&fcinfo, sstate->fxprstate.args, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("op ANY/ALL (array) does not support set arguments"))); Assert(fcinfo.nargs == 2); /* * If the array is NULL then we return NULL --- it's not very * meaningful to do anything else, even if the operator isn't strict. */ if (fcinfo.argnull[1]) { *isNull = true; return (Datum) 0; } /* Else okay to fetch and detoast the array */ arr = DatumGetArrayTypeP(fcinfo.arg[1]); /* * If the array is empty, we return either FALSE or TRUE per the useOr * flag. This is correct even if the scalar is NULL; since we would * evaluate the operator zero times, it matters not whether it would * want to return NULL. */ nitems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); if (nitems <= 0) return BoolGetDatum(!useOr); /* * If the scalar is NULL, and the function is strict, return NULL. * This is just to avoid having to test for strictness inside the * loop. (XXX but if arrays could have null elements, we'd need a * test anyway.) */ if (fcinfo.argnull[0] && sstate->fxprstate.func.fn_strict) { *isNull = true; return (Datum) 0; } /* * We arrange to look up info about the element type only once per * series of calls, assuming the element type doesn't change * underneath us. */ if (sstate->element_type != ARR_ELEMTYPE(arr)) { get_typlenbyvalalign(ARR_ELEMTYPE(arr), &sstate->typlen, &sstate->typbyval, &sstate->typalign); sstate->element_type = ARR_ELEMTYPE(arr); } typlen = sstate->typlen; typbyval = sstate->typbyval; typalign = sstate->typalign; result = BoolGetDatum(!useOr); resultnull = false; /* Loop over the array elements */ s = (char *) ARR_DATA_PTR(arr); for (i = 0; i < nitems; i++) { Datum elt; Datum thisresult; /* Get array element */ elt = fetch_att(s, typbyval, typlen); s = att_addlength(s, typlen, PointerGetDatum(s)); s = (char *) att_align(s, typalign); /* Call comparison function */ fcinfo.arg[1] = elt; fcinfo.argnull[1] = false; fcinfo.isnull = false; thisresult = FunctionCallInvoke(&fcinfo); /* Combine results per OR or AND semantics */ if (fcinfo.isnull) resultnull = true; else if (useOr) { if (DatumGetBool(thisresult)) { result = BoolGetDatum(true); resultnull = false; break; /* needn't look at any more elements */ } } else { if (!DatumGetBool(thisresult)) { result = BoolGetDatum(false); resultnull = false; break; /* needn't look at any more elements */ } } } *isNull = resultnull; return result; } /* ---------------------------------------------------------------- * ExecEvalNot * ExecEvalOr * ExecEvalAnd * * Evaluate boolean expressions, with appropriate short-circuiting. * * The query planner reformulates clause expressions in the * qualification to conjunctive normal form. If we ever get * an AND to evaluate, we can be sure that it's not a top-level * clause in the qualification, but appears lower (as a function * argument, for example), or in the target list. Not that you * need to know this, mind you... * ---------------------------------------------------------------- */ static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { ExprState *clause = linitial(notclause->args); Datum expr_value; if (isDone) *isDone = ExprSingleResult; expr_value = ExecEvalExpr(clause, econtext, isNull, NULL); /* * if the expression evaluates to null, then we just cascade the null * back to whoever called us. */ if (*isNull) return expr_value; /* * evaluation of 'not' is simple.. expr is false, then return 'true' * and vice versa. */ return BoolGetDatum(!DatumGetBool(expr_value)); } /* ---------------------------------------------------------------- * ExecEvalOr * ---------------------------------------------------------------- */ static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { List *clauses = orExpr->args; ListCell *clause; bool AnyNull; if (isDone) *isDone = ExprSingleResult; AnyNull = false; /* * If any of the clauses is TRUE, the OR result is TRUE regardless of * the states of the rest of the clauses, so we can stop evaluating * and return TRUE immediately. If none are TRUE and one or more is * NULL, we return NULL; otherwise we return FALSE. This makes sense * when you interpret NULL as "don't know": if we have a TRUE then the * OR is TRUE even if we aren't sure about some of the other inputs. * If all the known inputs are FALSE, but we have one or more "don't * knows", then we have to report that we "don't know" what the OR's * result should be --- perhaps one of the "don't knows" would have * been TRUE if we'd known its value. Only when all the inputs are * known to be FALSE can we state confidently that the OR's result is * FALSE. */ foreach(clause, clauses) { ExprState *clausestate = (ExprState *) lfirst(clause); Datum clause_value; clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); /* * if we have a non-null true result, then return it. */ if (*isNull) AnyNull = true; /* remember we got a null */ else if (DatumGetBool(clause_value)) return clause_value; } /* AnyNull is true if at least one clause evaluated to NULL */ *isNull = AnyNull; return BoolGetDatum(false); } /* ---------------------------------------------------------------- * ExecEvalAnd * ---------------------------------------------------------------- */ static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { List *clauses = andExpr->args; ListCell *clause; bool AnyNull; if (isDone) *isDone = ExprSingleResult; AnyNull = false; /* * If any of the clauses is FALSE, the AND result is FALSE regardless * of the states of the rest of the clauses, so we can stop evaluating * and return FALSE immediately. If none are FALSE and one or more is * NULL, we return NULL; otherwise we return TRUE. This makes sense * when you interpret NULL as "don't know", using the same sort of * reasoning as for OR, above. */ foreach(clause, clauses) { ExprState *clausestate = (ExprState *) lfirst(clause); Datum clause_value; clause_value = ExecEvalExpr(clausestate, econtext, isNull, NULL); /* * if we have a non-null false result, then return it. */ if (*isNull) AnyNull = true; /* remember we got a null */ else if (!DatumGetBool(clause_value)) return clause_value; } /* AnyNull is true if at least one clause evaluated to NULL */ *isNull = AnyNull; return BoolGetDatum(!AnyNull); } /* ---------------------------------------------------------------- * ExecEvalCase * * Evaluate a CASE clause. Will have boolean expressions * inside the WHEN clauses, and will have expressions * for results. * - thomas 1998-11-09 * ---------------------------------------------------------------- */ static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { List *clauses = caseExpr->args; ListCell *clause; Datum save_datum; bool save_isNull; if (isDone) *isDone = ExprSingleResult; /* * If there's a test expression, we have to evaluate it and save the * value where the CaseTestExpr placeholders can find it. We must save * and restore prior setting of econtext's caseValue fields, in case * this node is itself within a larger CASE. */ save_datum = econtext->caseValue_datum; save_isNull = econtext->caseValue_isNull; if (caseExpr->arg) { econtext->caseValue_datum = ExecEvalExpr(caseExpr->arg, econtext, &econtext->caseValue_isNull, NULL); } /* * we evaluate each of the WHEN clauses in turn, as soon as one is * true we return the corresponding result. If none are true then we * return the value of the default clause, or NULL if there is none. */ foreach(clause, clauses) { CaseWhenState *wclause = lfirst(clause); Datum clause_value; clause_value = ExecEvalExpr(wclause->expr, econtext, isNull, NULL); /* * if we have a true test, then we return the result, since the * case statement is satisfied. A NULL result from the test is * not considered true. */ if (DatumGetBool(clause_value) && !*isNull) { econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; return ExecEvalExpr(wclause->result, econtext, isNull, isDone); } } econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; if (caseExpr->defresult) { return ExecEvalExpr(caseExpr->defresult, econtext, isNull, isDone); } *isNull = true; return (Datum) 0; } /* * ExecEvalCaseTestExpr * * Return the value stored by CASE. */ static Datum ExecEvalCaseTestExpr(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { if (isDone) *isDone = ExprSingleResult; *isNull = econtext->caseValue_isNull; return econtext->caseValue_datum; } /* ---------------------------------------------------------------- * ExecEvalArray - ARRAY[] expressions * * NOTE: currently, if any input value is NULL then we return a NULL array, * so the ARRAY[] construct can be considered strict. Eventually this will * change; when it does, be sure to fix contain_nonstrict_functions(). * ---------------------------------------------------------------- */ static Datum ExecEvalArray(ArrayExprState *astate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { ArrayExpr *arrayExpr = (ArrayExpr *) astate->xprstate.expr; ArrayType *result; ListCell *element; Oid element_type = arrayExpr->element_typeid; int ndims = 0; int dims[MAXDIM]; int lbs[MAXDIM]; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone) *isDone = ExprSingleResult; if (!arrayExpr->multidims) { /* Elements are presumably of scalar type */ int nelems; Datum *dvalues; int i = 0; ndims = 1; nelems = list_length(astate->elements); /* Shouldn't happen here, but if length is 0, return NULL */ if (nelems == 0) { *isNull = true; return (Datum) 0; } dvalues = (Datum *) palloc(nelems * sizeof(Datum)); /* loop through and build array of datums */ foreach(element, astate->elements) { ExprState *e = (ExprState *) lfirst(element); bool eisnull; dvalues[i++] = ExecEvalExpr(e, econtext, &eisnull, NULL); if (eisnull) { *isNull = true; return (Datum) 0; } } /* setup for 1-D array of the given length */ dims[0] = nelems; lbs[0] = 1; result = construct_md_array(dvalues, ndims, dims, lbs, element_type, astate->elemlength, astate->elembyval, astate->elemalign); } else { /* Must be nested array expressions */ char *dat = NULL; Size ndatabytes = 0; int nbytes; int outer_nelems = list_length(astate->elements); int elem_ndims = 0; int *elem_dims = NULL; int *elem_lbs = NULL; bool firstone = true; int i; /* loop through and get data area from each element */ foreach(element, astate->elements) { ExprState *e = (ExprState *) lfirst(element); bool eisnull; Datum arraydatum; ArrayType *array; int elem_ndatabytes; arraydatum = ExecEvalExpr(e, econtext, &eisnull, NULL); if (eisnull) { *isNull = true; return (Datum) 0; } array = DatumGetArrayTypeP(arraydatum); /* run-time double-check on element type */ if (element_type != ARR_ELEMTYPE(array)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot merge incompatible arrays"), errdetail("Array with element type %s cannot be " "included in ARRAY construct with element type %s.", format_type_be(ARR_ELEMTYPE(array)), format_type_be(element_type)))); if (firstone) { /* Get sub-array details from first member */ elem_ndims = ARR_NDIM(array); ndims = elem_ndims + 1; if (ndims <= 0 || ndims > MAXDIM) ereport(ERROR, (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), errmsg("number of array dimensions (%d) exceeds " \ "the maximum allowed (%d)", ndims, MAXDIM))); elem_dims = (int *) palloc(elem_ndims * sizeof(int)); memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)); elem_lbs = (int *) palloc(elem_ndims * sizeof(int)); memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)); firstone = false; } else { /* Check other sub-arrays are compatible */ if (elem_ndims != ARR_NDIM(array) || memcmp(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int)) != 0 || memcmp(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int)) != 0) ereport(ERROR, (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), errmsg("multidimensional arrays must have array " "expressions with matching dimensions"))); } elem_ndatabytes = ARR_SIZE(array) - ARR_OVERHEAD(elem_ndims); ndatabytes += elem_ndatabytes; if (dat == NULL) dat = (char *) palloc(ndatabytes); else dat = (char *) repalloc(dat, ndatabytes); memcpy(dat + (ndatabytes - elem_ndatabytes), ARR_DATA_PTR(array), elem_ndatabytes); } /* setup for multi-D array */ dims[0] = outer_nelems; lbs[0] = 1; for (i = 1; i < ndims; i++) { dims[i] = elem_dims[i - 1]; lbs[i] = elem_lbs[i - 1]; } nbytes = ndatabytes + ARR_OVERHEAD(ndims); result = (ArrayType *) palloc(nbytes); result->size = nbytes; result->ndim = ndims; result->flags = 0; result->elemtype = element_type; memcpy(ARR_DIMS(result), dims, ndims * sizeof(int)); memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int)); if (ndatabytes > 0) memcpy(ARR_DATA_PTR(result), dat, ndatabytes); if (dat != NULL) pfree(dat); } return PointerGetDatum(result); } /* ---------------------------------------------------------------- * ExecEvalRow - ROW() expressions * ---------------------------------------------------------------- */ static Datum ExecEvalRow(RowExprState *rstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { HeapTuple tuple; Datum *values; char *nulls; int natts; ListCell *arg; int i; /* Set default values for result flags: non-null, not a set result */ *isNull = false; if (isDone) *isDone = ExprSingleResult; /* Allocate workspace */ natts = rstate->tupdesc->natts; values = (Datum *) palloc0(natts * sizeof(Datum)); nulls = (char *) palloc(natts * sizeof(char)); /* preset to nulls in case rowtype has some later-added columns */ memset(nulls, 'n', natts * sizeof(char)); /* Evaluate field values */ i = 0; foreach(arg, rstate->args) { ExprState *e = (ExprState *) lfirst(arg); bool eisnull; values[i] = ExecEvalExpr(e, econtext, &eisnull, NULL); nulls[i] = eisnull ? 'n' : ' '; i++; } tuple = heap_formtuple(rstate->tupdesc, values, nulls); pfree(values); pfree(nulls); return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- * ExecEvalCoalesce * ---------------------------------------------------------------- */ static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { ListCell *arg; if (isDone) *isDone = ExprSingleResult; /* Simply loop through until something NOT NULL is found */ foreach(arg, coalesceExpr->args) { ExprState *e = (ExprState *) lfirst(arg); Datum value; value = ExecEvalExpr(e, econtext, isNull, NULL); if (!*isNull) return value; } /* Else return NULL */ *isNull = true; return (Datum) 0; } /* ---------------------------------------------------------------- * ExecEvalNullIf * * Note that this is *always* derived from the equals operator, * but since we need special processing of the arguments * we can not simply reuse ExecEvalOper() or ExecEvalFunc(). * ---------------------------------------------------------------- */ static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Datum result; FunctionCallInfoData fcinfo; ExprDoneCond argDone; List *argList; if (isDone) *isDone = ExprSingleResult; /* * Initialize function cache if first time through */ if (nullIfExpr->func.fn_oid == InvalidOid) { NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr; init_fcache(op->opfuncid, nullIfExpr, econtext->ecxt_per_query_memory); Assert(!nullIfExpr->func.fn_retset); } /* * extract info from nullIfExpr */ argList = nullIfExpr->args; /* Need to prep callinfo structure */ MemSet(&fcinfo, 0, sizeof(fcinfo)); fcinfo.flinfo = &(nullIfExpr->func); argDone = ExecEvalFuncArgs(&fcinfo, argList, econtext); if (argDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("NULLIF does not support set arguments"))); Assert(fcinfo.nargs == 2); /* if either argument is NULL they can't be equal */ if (!fcinfo.argnull[0] && !fcinfo.argnull[1]) { fcinfo.isnull = false; result = FunctionCallInvoke(&fcinfo); /* if the arguments are equal return null */ if (!fcinfo.isnull && DatumGetBool(result)) { *isNull = true; return (Datum) 0; } } /* else return first argument */ *isNull = fcinfo.argnull[0]; return fcinfo.arg[0]; } /* ---------------------------------------------------------------- * ExecEvalNullTest * * Evaluate a NullTest node. * ---------------------------------------------------------------- */ static Datum ExecEvalNullTest(GenericExprState *nstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { NullTest *ntest = (NullTest *) nstate->xprstate.expr; Datum result; result = ExecEvalExpr(nstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ switch (ntest->nulltesttype) { case IS_NULL: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else return BoolGetDatum(false); case IS_NOT_NULL: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else return BoolGetDatum(true); default: elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); return (Datum) 0; /* keep compiler quiet */ } } /* ---------------------------------------------------------------- * ExecEvalBooleanTest * * Evaluate a BooleanTest node. * ---------------------------------------------------------------- */ static Datum ExecEvalBooleanTest(GenericExprState *bstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr; Datum result; result = ExecEvalExpr(bstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ switch (btest->booltesttype) { case IS_TRUE: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else if (DatumGetBool(result)) return BoolGetDatum(true); else return BoolGetDatum(false); case IS_NOT_TRUE: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else if (DatumGetBool(result)) return BoolGetDatum(false); else return BoolGetDatum(true); case IS_FALSE: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else if (DatumGetBool(result)) return BoolGetDatum(false); else return BoolGetDatum(true); case IS_NOT_FALSE: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else if (DatumGetBool(result)) return BoolGetDatum(true); else return BoolGetDatum(false); case IS_UNKNOWN: if (*isNull) { *isNull = false; return BoolGetDatum(true); } else return BoolGetDatum(false); case IS_NOT_UNKNOWN: if (*isNull) { *isNull = false; return BoolGetDatum(false); } else return BoolGetDatum(true); default: elog(ERROR, "unrecognized booltesttype: %d", (int) btest->booltesttype); return (Datum) 0; /* keep compiler quiet */ } } /* * ExecEvalCoerceToDomain * * Test the provided data against the domain constraint(s). If the data * passes the constraint specifications, pass it through (return the * datum) otherwise throw an error. */ static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr; Datum result; ListCell *l; result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return result; /* nothing to check */ foreach(l, cstate->constraints) { DomainConstraintState *con = (DomainConstraintState *) lfirst(l); switch (con->constrainttype) { case DOM_CONSTRAINT_NOTNULL: if (*isNull) ereport(ERROR, (errcode(ERRCODE_NOT_NULL_VIOLATION), errmsg("domain %s does not allow null values", format_type_be(ctest->resulttype)))); break; case DOM_CONSTRAINT_CHECK: { Datum conResult; bool conIsNull; Datum save_datum; bool save_isNull; /* * Set up value to be returned by CoerceToDomainValue * nodes. We must save and restore prior setting of * econtext's domainValue fields, in case this node is * itself within a check expression for another * domain. */ save_datum = econtext->domainValue_datum; save_isNull = econtext->domainValue_isNull; econtext->domainValue_datum = result; econtext->domainValue_isNull = *isNull; conResult = ExecEvalExpr(con->check_expr, econtext, &conIsNull, NULL); if (!conIsNull && !DatumGetBool(conResult)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("value for domain %s violates check constraint \"%s\"", format_type_be(ctest->resulttype), con->name))); econtext->domainValue_datum = save_datum; econtext->domainValue_isNull = save_isNull; break; } default: elog(ERROR, "unrecognized constraint type: %d", (int) con->constrainttype); break; } } /* If all has gone well (constraints did not fail) return the datum */ return result; } /* * ExecEvalCoerceToDomainValue * * Return the value stored by CoerceToDomain. */ static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { if (isDone) *isDone = ExprSingleResult; *isNull = econtext->domainValue_isNull; return econtext->domainValue_datum; } /* ---------------------------------------------------------------- * ExecEvalFieldSelect * * Evaluate a FieldSelect node. * ---------------------------------------------------------------- */ static Datum ExecEvalFieldSelect(FieldSelectState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr; Datum result; Datum tupDatum; HeapTupleHeader tuple; Oid tupType; int32 tupTypmod; TupleDesc tupDesc; HeapTupleData tmptup; tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); /* this test covers the isDone exception too: */ if (*isNull) return tupDatum; tuple = DatumGetHeapTupleHeader(tupDatum); tupType = HeapTupleHeaderGetTypeId(tuple); tupTypmod = HeapTupleHeaderGetTypMod(tuple); /* Lookup tupdesc if first time through or if type changes */ tupDesc = fstate->argdesc; if (tupDesc == NULL || tupType != tupDesc->tdtypeid || tupTypmod != tupDesc->tdtypmod) { MemoryContext oldcontext; tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod); /* Copy the tupdesc into query storage for safety */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupDesc = CreateTupleDescCopy(tupDesc); if (fstate->argdesc) FreeTupleDesc(fstate->argdesc); fstate->argdesc = tupDesc; MemoryContextSwitchTo(oldcontext); } /* * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set * all the fields in the struct just in case user tries to inspect * system columns. */ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = tuple; result = heap_getattr(&tmptup, fselect->fieldnum, tupDesc, isNull); return result; } /* ---------------------------------------------------------------- * ExecEvalFieldStore * * Evaluate a FieldStore node. * ---------------------------------------------------------------- */ static Datum ExecEvalFieldStore(FieldStoreState *fstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { FieldStore *fstore = (FieldStore *) fstate->xprstate.expr; HeapTuple tuple; Datum tupDatum; TupleDesc tupDesc; Datum *values; char *nulls; Datum save_datum; bool save_isNull; ListCell *l1, *l2; tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull, isDone); if (isDone && *isDone == ExprEndResult) return tupDatum; /* Lookup tupdesc if first time through or if type changes */ tupDesc = fstate->argdesc; if (tupDesc == NULL || fstore->resulttype != tupDesc->tdtypeid) { MemoryContext oldcontext; tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1); /* Copy the tupdesc into query storage for safety */ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory); tupDesc = CreateTupleDescCopy(tupDesc); if (fstate->argdesc) FreeTupleDesc(fstate->argdesc); fstate->argdesc = tupDesc; MemoryContextSwitchTo(oldcontext); } /* Allocate workspace */ values = (Datum *) palloc(tupDesc->natts * sizeof(Datum)); nulls = (char *) palloc(tupDesc->natts * sizeof(char)); if (!*isNull) { /* * heap_deformtuple needs a HeapTuple not a bare HeapTupleHeader. * We set all the fields in the struct just in case. */ HeapTupleHeader tuphdr; HeapTupleData tmptup; tuphdr = DatumGetHeapTupleHeader(tupDatum); tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr); ItemPointerSetInvalid(&(tmptup.t_self)); tmptup.t_tableOid = InvalidOid; tmptup.t_data = tuphdr; heap_deformtuple(&tmptup, tupDesc, values, nulls); } else { /* Convert null input tuple into an all-nulls row */ memset(nulls, 'n', tupDesc->natts * sizeof(char)); } /* Result is never null */ *isNull = false; save_datum = econtext->caseValue_datum; save_isNull = econtext->caseValue_isNull; forboth(l1, fstate->newvals, l2, fstore->fieldnums) { ExprState *newval = (ExprState *) lfirst(l1); AttrNumber fieldnum = lfirst_int(l2); bool eisnull; Assert(fieldnum > 0 && fieldnum <= tupDesc->natts); /* * Use the CaseTestExpr mechanism to pass down the old value of * the field being replaced; this is useful in case we have a * nested field update situation. It's safe to reuse the CASE * mechanism because there cannot be a CASE between here and where * the value would be needed. */ econtext->caseValue_datum = values[fieldnum - 1]; econtext->caseValue_isNull = (nulls[fieldnum - 1] == 'n'); values[fieldnum - 1] = ExecEvalExpr(newval, econtext, &eisnull, NULL); nulls[fieldnum - 1] = eisnull ? 'n' : ' '; } econtext->caseValue_datum = save_datum; econtext->caseValue_isNull = save_isNull; tuple = heap_formtuple(tupDesc, values, nulls); pfree(values); pfree(nulls); return HeapTupleGetDatum(tuple); } /* ---------------------------------------------------------------- * ExecEvalRelabelType * * Evaluate a RelabelType node. * ---------------------------------------------------------------- */ static Datum ExecEvalRelabelType(GenericExprState *exprstate, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { return ExecEvalExpr(exprstate->arg, econtext, isNull, isDone); } /* * ExecEvalExprSwitchContext * * Same as ExecEvalExpr, but get into the right allocation context explicitly. */ Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext, bool *isNull, ExprDoneCond *isDone) { Datum retDatum; MemoryContext oldContext; oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); retDatum = ExecEvalExpr(expression, econtext, isNull, isDone); MemoryContextSwitchTo(oldContext); return retDatum; } /* * 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 SubPlan 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. * * 'parent' may be NULL if we are preparing an expression that is not * associated with a plan tree. (If so, it can't have aggs or subplans.) * This case should usually come through ExecPrepareExpr, not directly here. */ ExprState * ExecInitExpr(Expr *node, PlanState *parent) { ExprState *state; if (node == NULL) return NULL; /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); switch (nodeTag(node)) { case T_Var: state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalVar; break; case T_Const: state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalConst; break; case T_Param: state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalParam; break; case T_CoerceToDomainValue: state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalCoerceToDomainValue; break; case T_CaseTestExpr: state = (ExprState *) makeNode(ExprState); state->evalfunc = ExecEvalCaseTestExpr; break; case T_Aggref: { Aggref *aggref = (Aggref *) node; AggrefExprState *astate = makeNode(AggrefExprState); astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref; if (parent && IsA(parent, AggState)) { AggState *aggstate = (AggState *) parent; int naggs; aggstate->aggs = lcons(astate, aggstate->aggs); naggs = ++aggstate->numaggs; astate->target = ExecInitExpr(aggref->target, parent); /* * Complain if the aggregate's argument contains any * aggregates; nested agg functions are semantically * nonsensical. (This should have been caught * earlier, but we defend against it here anyway.) */ if (naggs != aggstate->numaggs) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("aggregate function calls may not be nested"))); } else { /* planner messed up */ elog(ERROR, "aggref found in non-Agg plan node"); } state = (ExprState *) astate; } break; case T_ArrayRef: { ArrayRef *aref = (ArrayRef *) node; ArrayRefExprState *astate = makeNode(ArrayRefExprState); astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef; 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); /* do one-time catalog lookups for type info */ astate->refattrlength = get_typlen(aref->refarraytype); get_typlenbyvalalign(aref->refelemtype, &astate->refelemlength, &astate->refelembyval, &astate->refelemalign); state = (ExprState *) astate; } break; case T_FuncExpr: { FuncExpr *funcexpr = (FuncExpr *) node; FuncExprState *fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc; 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); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper; 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); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct; fstate->args = (List *) ExecInitExpr((Expr *) distinctexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState *) fstate; } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node; ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState); sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp; sstate->fxprstate.args = (List *) ExecInitExpr((Expr *) opexpr->args, parent); sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */ sstate->element_type = InvalidOid; /* ditto */ state = (ExprState *) sstate; } break; case T_BoolExpr: { BoolExpr *boolexpr = (BoolExpr *) node; BoolExprState *bstate = makeNode(BoolExprState); switch (boolexpr->boolop) { case AND_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAnd; break; case OR_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOr; break; case NOT_EXPR: bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNot; break; default: elog(ERROR, "unrecognized boolop: %d", (int) boolexpr->boolop); break; } bstate->args = (List *) ExecInitExpr((Expr *) boolexpr->args, parent); state = (ExprState *) bstate; } break; case T_SubPlan: { /* Keep this in sync with ExecInitExprInitPlan, below */ SubPlan *subplan = (SubPlan *) node; SubPlanState *sstate = makeNode(SubPlanState); sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan; if (!parent) elog(ERROR, "SubPlan found with no parent plan"); /* * Here we just add the SubPlanState nodes to * parent->subPlan. The subplans will be initialized * later. */ parent->subPlan = lcons(sstate, parent->subPlan); sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->exprs = (List *) ExecInitExpr((Expr *) subplan->exprs, parent); sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent); state = (ExprState *) sstate; } break; case T_FieldSelect: { FieldSelect *fselect = (FieldSelect *) node; FieldSelectState *fstate = makeNode(FieldSelectState); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect; fstate->arg = ExecInitExpr(fselect->arg, parent); fstate->argdesc = NULL; state = (ExprState *) fstate; } break; case T_FieldStore: { FieldStore *fstore = (FieldStore *) node; FieldStoreState *fstate = makeNode(FieldStoreState); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore; fstate->arg = ExecInitExpr(fstore->arg, parent); fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent); fstate->argdesc = NULL; state = (ExprState *) fstate; } break; case T_RelabelType: { RelabelType *relabel = (RelabelType *) node; GenericExprState *gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType; gstate->arg = ExecInitExpr(relabel->arg, parent); state = (ExprState *) gstate; } break; case T_CaseExpr: { CaseExpr *caseexpr = (CaseExpr *) node; CaseExprState *cstate = makeNode(CaseExprState); List *outlist = NIL; ListCell *l; cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase; cstate->arg = ExecInitExpr(caseexpr->arg, parent); foreach(l, caseexpr->args) { CaseWhen *when = (CaseWhen *) lfirst(l); CaseWhenState *wstate = makeNode(CaseWhenState); Assert(IsA(when, CaseWhen)); wstate->xprstate.evalfunc = NULL; /* not used */ wstate->xprstate.expr = (Expr *) when; wstate->expr = ExecInitExpr(when->expr, parent); wstate->result = ExecInitExpr(when->result, parent); outlist = lappend(outlist, wstate); } cstate->args = outlist; cstate->defresult = ExecInitExpr(caseexpr->defresult, parent); state = (ExprState *) cstate; } break; case T_ArrayExpr: { ArrayExpr *arrayexpr = (ArrayExpr *) node; ArrayExprState *astate = makeNode(ArrayExprState); List *outlist = NIL; ListCell *l; astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArray; foreach(l, arrayexpr->elements) { Expr *e = (Expr *) lfirst(l); ExprState *estate; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } astate->elements = outlist; /* do one-time catalog lookup for type info */ get_typlenbyvalalign(arrayexpr->element_typeid, &astate->elemlength, &astate->elembyval, &astate->elemalign); state = (ExprState *) astate; } break; case T_RowExpr: { RowExpr *rowexpr = (RowExpr *) node; RowExprState *rstate = makeNode(RowExprState); Form_pg_attribute *attrs; List *outlist = NIL; ListCell *l; int i; rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow; /* Build tupdesc to describe result tuples */ if (rowexpr->row_typeid == RECORDOID) { /* generic record, use runtime type assignment */ rstate->tupdesc = ExecTypeFromExprList(rowexpr->args); rstate->tupdesc = BlessTupleDesc(rstate->tupdesc); } else { /* it's been cast to a named type, use that */ rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc); } /* Set up evaluation, skipping any deleted columns */ Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts); attrs = rstate->tupdesc->attrs; i = 0; foreach(l, rowexpr->args) { Expr *e = (Expr *) lfirst(l); ExprState *estate; if (!attrs[i]->attisdropped) { /* * Guard against ALTER COLUMN TYPE on rowtype * since the RowExpr was created. XXX should we * check typmod too? Not sure we can be sure * it'll be the same. */ if (exprType((Node *) e) != attrs[i]->atttypid) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("ROW() column has type %s instead of type %s", format_type_be(exprType((Node *) e)), format_type_be(attrs[i]->atttypid)))); } else { /* * Ignore original expression and insert a NULL. * We don't really care what type of NULL it is, * so always make an int4 NULL. */ e = (Expr *) makeNullConst(INT4OID); } estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); i++; } rstate->args = outlist; state = (ExprState *) rstate; } break; case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; CoalesceExprState *cstate = makeNode(CoalesceExprState); List *outlist = NIL; ListCell *l; cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoalesce; foreach(l, coalesceexpr->args) { Expr *e = (Expr *) lfirst(l); ExprState *estate; estate = ExecInitExpr(e, parent); outlist = lappend(outlist, estate); } cstate->args = outlist; state = (ExprState *) cstate; } break; case T_NullIfExpr: { NullIfExpr *nullifexpr = (NullIfExpr *) node; FuncExprState *fstate = makeNode(FuncExprState); fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf; fstate->args = (List *) ExecInitExpr((Expr *) nullifexpr->args, parent); fstate->func.fn_oid = InvalidOid; /* not initialized */ state = (ExprState *) fstate; } break; case T_NullTest: { NullTest *ntest = (NullTest *) node; GenericExprState *gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest; gstate->arg = ExecInitExpr(ntest->arg, parent); state = (ExprState *) gstate; } break; case T_BooleanTest: { BooleanTest *btest = (BooleanTest *) node; GenericExprState *gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest; gstate->arg = ExecInitExpr(btest->arg, parent); state = (ExprState *) gstate; } break; case T_CoerceToDomain: { CoerceToDomain *ctest = (CoerceToDomain *) node; CoerceToDomainState *cstate = makeNode(CoerceToDomainState); cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain; cstate->arg = ExecInitExpr(ctest->arg, parent); cstate->constraints = GetDomainConstraints(ctest->resulttype); state = (ExprState *) cstate; } break; case T_TargetEntry: { TargetEntry *tle = (TargetEntry *) node; GenericExprState *gstate = makeNode(GenericExprState); gstate->xprstate.evalfunc = NULL; /* not used */ gstate->arg = ExecInitExpr(tle->expr, parent); state = (ExprState *) gstate; } break; case T_List: { List *outlist = NIL; ListCell *l; foreach(l, (List *) node) { outlist = lappend(outlist, ExecInitExpr((Expr *) lfirst(l), parent)); } /* Don't fall through to the "common" code below */ return (ExprState *) outlist; } default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); state = NULL; /* keep compiler quiet */ break; } /* 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. */ SubPlanState * ExecInitExprInitPlan(SubPlan *node, PlanState *parent) { SubPlanState *sstate = makeNode(SubPlanState); if (!parent) elog(ERROR, "SubPlan found with no parent plan"); /* The subplan's state will be initialized later */ sstate->sub_estate = NULL; sstate->planstate = NULL; sstate->exprs = (List *) ExecInitExpr((Expr *) node->exprs, parent); sstate->args = (List *) ExecInitExpr((Expr *) node->args, parent); sstate->xprstate.expr = (Expr *) node; return sstate; } /* * ExecPrepareExpr --- initialize for expression execution outside a normal * Plan tree context. * * This differs from ExecInitExpr in that we don't assume the caller is * already running in the EState's per-query context. Also, we apply * fix_opfuncids() to the passed expression tree to be sure it is ready * to run. (In ordinary Plan trees the planner will have fixed opfuncids, * but callers outside the executor will not have done this.) */ ExprState * ExecPrepareExpr(Expr *node, EState *estate) { ExprState *result; MemoryContext oldcontext; fix_opfuncids((Node *) node); oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); result = ExecInitExpr(node, NULL); MemoryContextSwitchTo(oldcontext); return result; } /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- * ExecQual * * Evaluates a conjunctive boolean expression (qual list) and * returns true iff none of the subexpressions are false. * (We also return true if the list is empty.) * * If some of the subexpressions yield NULL but none yield FALSE, * then the result of the conjunction is NULL (ie, unknown) * according to three-valued boolean logic. In this case, * we return the value specified by the "resultForNull" parameter. * * Callers evaluating WHERE clauses should pass resultForNull=FALSE, * since SQL specifies that tuples with null WHERE results do not * get selected. On the other hand, callers evaluating constraint * conditions should pass resultForNull=TRUE, since SQL also specifies * that NULL constraint conditions are not failures. * * NOTE: it would not be correct to use this routine to evaluate an * AND subclause of a boolean expression; for that purpose, a NULL * result must be returned as NULL so that it can be properly treated * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). * This routine is only used in contexts where a complete expression * is being evaluated and we know that NULL can be treated the same * as one boolean result or the other. * * ---------------------------------------------------------------- */ bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull) { bool result; MemoryContext oldContext; ListCell *l; /* * debugging stuff */ EV_printf("ExecQual: qual is "); EV_nodeDisplay(qual); EV_printf("\n"); IncrProcessed(); /* * Run in short-lived per-tuple context while computing expressions. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * Evaluate the qual conditions one at a time. If we find a FALSE * result, we can stop evaluating and return FALSE --- the AND result * must be FALSE. Also, if we find a NULL result when resultForNull * is FALSE, we can stop and return FALSE --- the AND result must be * FALSE or NULL in that case, and the caller doesn't care which. * * If we get to the end of the list, we can return TRUE. This will * happen when the AND result is indeed TRUE, or when the AND result * is NULL (one or more NULL subresult, with all the rest TRUE) and * the caller has specified resultForNull = TRUE. */ result = true; foreach(l, qual) { ExprState *clause = (ExprState *) lfirst(l); Datum expr_value; bool isNull; expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); if (isNull) { if (resultForNull == false) { result = false; /* treat NULL as FALSE */ break; } } else { if (!DatumGetBool(expr_value)) { result = false; /* definitely FALSE */ break; } } } MemoryContextSwitchTo(oldContext); return result; } /* * Number of items in a tlist (including any resjunk items!) */ int ExecTargetListLength(List *targetlist) { /* This used to be more complex, but fjoins are dead */ return list_length(targetlist); } /* * Number of items in a tlist, not including any resjunk items */ int ExecCleanTargetListLength(List *targetlist) { int len = 0; ListCell *tl; foreach(tl, targetlist) { TargetEntry *curTle = (TargetEntry *) lfirst(tl); Assert(IsA(curTle, TargetEntry)); if (!curTle->resdom->resjunk) len++; } return len; } /* ---------------------------------------------------------------- * ExecTargetList * * Evaluates a targetlist with respect to the given * expression context and returns a tuple. * * The caller must pass workspace for the values and nulls arrays * as well as the itemIsDone array. This convention saves palloc'ing * workspace on each call, and some callers may find it useful to examine * the values array directly. * * 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 ExecTargetList(List *targetlist, TupleDesc targettype, ExprContext *econtext, Datum *values, char *nulls, ExprDoneCond *itemIsDone, ExprDoneCond *isDone) { MemoryContext oldContext; ListCell *tl; bool isNull; bool haveDoneSets; /* * debugging stuff */ EV_printf("ExecTargetList: tl is "); EV_nodeDisplay(targetlist); EV_printf("\n"); /* * Run in short-lived per-tuple context while computing expressions. */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); /* * There used to be some klugy and demonstrably broken code here that * special-cased the situation where targetlist == NIL. Now we just * fall through and return an empty-but-valid tuple. */ /* * evaluate all the expressions in the target list */ if (isDone) *isDone = ExprSingleResult; /* until proven otherwise */ haveDoneSets = false; /* any exhausted set exprs in tlist? */ foreach(tl, targetlist) { GenericExprState *gstate = (GenericExprState *) lfirst(tl); TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; AttrNumber resind = tle->resdom->resno - 1; values[resind] = ExecEvalExpr(gstate->arg, econtext, &isNull, &itemIsDone[resind]); nulls[resind] = isNull ? 'n' : ' '; if (itemIsDone[resind] != ExprSingleResult) { /* We have a set-valued expression in the tlist */ if (isDone == NULL) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("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 { /* we have done sets in the tlist, set flag for that */ haveDoneSets = true; } } } if (haveDoneSets) { /* * note: can't get here unless we verified isDone != NULL */ if (*isDone == ExprSingleResult) { /* * all sets are done, so report that tlist expansion is * complete. */ *isDone = ExprEndResult; MemoryContextSwitchTo(oldContext); return NULL; } else { /* * We have some done and some undone sets. Restart the done * ones so that we can deliver a tuple (if possible). */ foreach(tl, targetlist) { GenericExprState *gstate = (GenericExprState *) lfirst(tl); TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; AttrNumber resind = tle->resdom->resno - 1; if (itemIsDone[resind] == ExprEndResult) { values[resind] = ExecEvalExpr(gstate->arg, 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; } } } /* * 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) { GenericExprState *gstate = (GenericExprState *) lfirst(tl); TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; AttrNumber resind = tle->resdom->resno - 1; while (itemIsDone[resind] == ExprMultipleResult) { (void) ExecEvalExpr(gstate->arg, econtext, &isNull, &itemIsDone[resind]); } } MemoryContextSwitchTo(oldContext); return NULL; } } } /* * form the new result tuple (in the caller's memory context!) */ MemoryContextSwitchTo(oldContext); return heap_formtuple(targettype, values, nulls); } /* ---------------------------------------------------------------- * ExecProject * * 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 * redundant projections by storing pointers to datums * in the tuple table and then passing these around when * possible. this should make things much quicker. * -cim 6/3/91 * ---------------------------------------------------------------- */ TupleTableSlot * ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) { TupleTableSlot *slot; TupleDesc tupType; HeapTuple newTuple; /* * sanity checks */ if (projInfo == NULL) return NULL; /* * get the projection info we want */ slot = projInfo->pi_slot; tupType = slot->ttc_tupleDescriptor; /* * form a new result tuple (if possible --- result can be NULL) */ newTuple = ExecTargetList(projInfo->pi_targetlist, tupType, projInfo->pi_exprContext, projInfo->pi_tupValues, projInfo->pi_tupNulls, projInfo->pi_itemIsDone, isDone); /* * store the tuple in the projection slot and return the slot. */ return ExecStoreTuple(newTuple, /* tuple to store */ slot, /* slot to store in */ InvalidBuffer, /* tuple has no buffer */ true); }