1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-24 10:47:04 +03:00
Tom Lane bee217924d Support expressions of the form 'scalar op ANY (array)' and
'scalar op ALL (array)', where the operator is applied between the
lefthand scalar and each element of the array.  The operator must
yield boolean; the result of the construct is the OR or AND of the
per-element results, respectively.

Original coding by Joe Conway, after an idea of Peter's.  Rewritten
by Tom to keep the implementation strictly separate from subqueries.
2003-06-29 00:33:44 +00:00

3039 lines
83 KiB
C

/*-------------------------------------------------------------------------
*
* execQual.c
* Routines to evaluate qualification and targetlist expressions
*
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/execQual.c,v 1.134 2003/06/29 00:33:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
/*
* INTERFACE ROUTINES
* ExecEvalExpr - evaluate an expression and return a datum
* ExecEvalExprSwitchContext - same, but switch into eval memory context
* ExecQual - return true/false if qualification is satisfied
* ExecProject - form a new tuple by projecting the given tuple
*
* NOTES
* ExecEvalExpr() and ExecEvalVar() are hotspots. making these faster
* will speed up the entire system. Unfortunately they are currently
* implemented recursively. Eliminating the recursion is bound to
* improve the speed of the executor.
*
* 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
*
*/
#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 "miscadmin.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"
/* static function decls */
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 ExecEvalParam(Param *expression, ExprContext *econtext,
bool *isNull);
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);
static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
ExprContext *econtext, bool *isNull);
static ExprDoneCond ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static Datum ExecEvalNot(BoolExprState *notclause, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalOr(BoolExprState *orExpr, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalAnd(BoolExprState *andExpr, ExprContext *econtext,
bool *isNull);
static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalArray(ArrayExprState *astate,
ExprContext *econtext,
bool *isNull);
static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
ExprContext *econtext,
bool *isNull);
static Datum ExecEvalNullIf(FuncExprState *nullIfExpr, ExprContext *econtext,
bool *isNull);
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(CoerceToDomainValue *conVal,
ExprContext *econtext, bool *isNull);
static Datum ExecEvalFieldSelect(GenericExprState *fstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
/*----------
* 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);
List *elt;
int i = 0,
j = 0;
IntArray upper,
lower;
int *lIndex;
if (arrayRef->refexpr != NULL)
{
array_source = (ArrayType *)
DatumGetPointer(ExecEvalExpr(astate->refexpr,
econtext,
isNull,
isDone));
/*
* If refexpr yields NULL, result is always NULL, for now anyway.
* (This means you cannot assign to an element or slice of an
* array that's NULL; it'll just stay NULL.)
*/
if (*isNull)
return (Datum) NULL;
}
else
{
/*
* Empty refexpr indicates we are doing an INSERT into an array
* column. For now, we just take the refassgnexpr (which the
* parser will have ensured is an array value) and return it
* as-is, ignoring any subscripts that may have been supplied in
* the INSERT column list. This is a kluge, but it's not real
* clear what the semantics ought to be...
*/
array_source = NULL;
}
foreach(elt, astate->refupperindexpr)
{
if (i >= MAXDIM)
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
upper.indx[i++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
econtext,
isNull,
NULL));
/* If any index expr yields NULL, result is NULL or source array */
if (*isNull)
{
if (!isAssignment || array_source == NULL)
return (Datum) NULL;
*isNull = false;
return PointerGetDatum(array_source);
}
}
if (astate->reflowerindexpr != NIL)
{
foreach(elt, astate->reflowerindexpr)
{
if (j >= MAXDIM)
elog(ERROR, "ExecEvalArrayRef: can only handle %d dimensions",
MAXDIM);
lower.indx[j++] = DatumGetInt32(ExecEvalExpr((ExprState *) lfirst(elt),
econtext,
isNull,
NULL));
/*
* If any index expr yields NULL, result is NULL or source
* array
*/
if (*isNull)
{
if (!isAssignment || array_source == NULL)
return (Datum) NULL;
*isNull = false;
return PointerGetDatum(array_source);
}
}
if (i != j)
elog(ERROR,
"ExecEvalArrayRef: upper and lower indices mismatch");
lIndex = lower.indx;
}
else
lIndex = NULL;
if (isAssignment)
{
Datum sourceData = ExecEvalExpr(astate->refassgnexpr,
econtext,
isNull,
NULL);
/*
* For now, can't cope with inserting NULL into an array, so make
* it a no-op per discussion above...
*/
if (*isNull)
{
if (array_source == NULL)
return (Datum) NULL;
*isNull = false;
return PointerGetDatum(array_source);
}
if (array_source == NULL)
return sourceData; /* XXX do something else? */
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)
{
if (econtext->ecxt_aggvalues == NULL) /* safety check */
elog(ERROR, "ExecEvalAggref: 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.
*
*
* As an entry condition, we expect that the datatype the
* plan expects to get (as told by our "variable" argument) is in
* fact the datatype of the attribute the plan says to fetch (as
* seen in the current context, identified by our "econtext"
* argument).
*
* If we fetch a Type A attribute and Caller treats it as if it
* were Type B, there will be undefined results (e.g. crash).
* One way these might mismatch now is that we're accessing a
* catalog class and the type information in the pg_attribute
* class does not match the hardcoded pg_attribute information
* (in pg_attribute.h) for the class in question.
*
* We have an Assert to make sure this entry condition is met.
*
* ---------------------------------------------------------------- */
static Datum
ExecEvalVar(Var *variable, ExprContext *econtext, bool *isNull)
{
Datum result;
TupleTableSlot *slot;
AttrNumber attnum;
HeapTuple heapTuple;
TupleDesc tuple_type;
/*
* get the slot we want
*/
switch (variable->varno)
{
case INNER: /* get the tuple from the inner node */
slot = econtext->ecxt_innertuple;
break;
case OUTER: /* get the tuple from the outer node */
slot = econtext->ecxt_outertuple;
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;
attnum = variable->varattno;
/* (See prolog for explanation of this Assert) */
Assert(attnum <= 0 ||
(attnum - 1 <= tuple_type->natts - 1 &&
tuple_type->attrs[attnum - 1] != NULL &&
variable->vartype == tuple_type->attrs[attnum - 1]->atttypid));
/*
* If the attribute number is invalid, then we are supposed to return
* the entire tuple; we give back a whole slot so that callers know
* what the tuple looks like.
*
* XXX this is a horrid crock: since the pointer to the slot might live
* longer than the current evaluation context, we are forced to copy
* the tuple and slot into a long-lived context --- we use
* the econtext's per-query memory which should be safe enough. This
* represents a serious memory leak if many such tuples are processed
* in one command, however. We ought to redesign the representation
* of whole-tuple datums so that this is not necessary.
*
* We assume it's OK to point to the existing tupleDescriptor, rather
* than copy that too.
*/
if (attnum == InvalidAttrNumber)
{
MemoryContext oldContext;
TupleTableSlot *tempSlot;
HeapTuple tup;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tempSlot = MakeTupleTableSlot();
tup = heap_copytuple(heapTuple);
ExecStoreTuple(tup, tempSlot, InvalidBuffer, true);
ExecSetSlotDescriptor(tempSlot, tuple_type, false);
MemoryContextSwitchTo(oldContext);
return PointerGetDatum(tempSlot);
}
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;
}
/* ----------------------------------------------------------------
* 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(Param *expression, ExprContext *econtext, bool *isNull)
{
int thisParamKind = expression->paramkind;
AttrNumber thisParamId = expression->paramid;
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.
* NOTE: The last entry in the param array is always an
* entry with kind == PARAM_INVALID.
*/
ParamListInfo paramList = econtext->ecxt_param_list_info;
char *thisParamName = expression->paramname;
bool matchFound = false;
if (paramList != NULL)
{
while (paramList->kind != PARAM_INVALID && !matchFound)
{
if (thisParamKind == paramList->kind)
{
switch (thisParamKind)
{
case PARAM_NAMED:
if (strcmp(paramList->name, thisParamName) == 0)
matchFound = true;
break;
case PARAM_NUM:
if (paramList->id == thisParamId)
matchFound = true;
break;
default:
elog(ERROR, "ExecEvalParam: invalid paramkind %d",
thisParamKind);
}
}
if (!matchFound)
paramList++;
} /* while */
} /* if */
if (!matchFound)
{
if (thisParamKind == PARAM_NAMED)
elog(ERROR, "ExecEvalParam: Unknown value for parameter %s",
thisParamName);
else
elog(ERROR, "ExecEvalParam: Unknown value for parameter %d",
thisParamId);
}
*isNull = paramList->isnull;
return paramList->value;
}
}
/* ----------------------------------------------------------------
* ExecEvalOper / ExecEvalFunc support routines
* ----------------------------------------------------------------
*/
/*
* GetAttributeByName
* GetAttributeByNum
*
* These are functions which return the value of the
* named attribute out of the tuple from the arg slot. User defined
* C functions which take a tuple as an argument are expected
* to use this. Ex: overpaid(EMP) might call GetAttributeByNum().
*/
Datum
GetAttributeByNum(TupleTableSlot *slot,
AttrNumber attrno,
bool *isNull)
{
Datum retval;
if (!AttributeNumberIsValid(attrno))
elog(ERROR, "GetAttributeByNum: Invalid attribute number");
if (!AttrNumberIsForUserDefinedAttr(attrno))
elog(ERROR, "GetAttributeByNum: cannot access system attributes here");
if (isNull == (bool *) NULL)
elog(ERROR, "GetAttributeByNum: a NULL isNull flag was passed");
if (TupIsNull(slot))
{
*isNull = true;
return (Datum) 0;
}
retval = heap_getattr(slot->val,
attrno,
slot->ttc_tupleDescriptor,
isNull);
if (*isNull)
return (Datum) 0;
return retval;
}
Datum
GetAttributeByName(TupleTableSlot *slot, char *attname, bool *isNull)
{
AttrNumber attrno;
TupleDesc tupdesc;
Datum retval;
int natts;
int i;
if (attname == NULL)
elog(ERROR, "GetAttributeByName: Invalid attribute name");
if (isNull == (bool *) NULL)
elog(ERROR, "GetAttributeByName: a NULL isNull flag was passed");
if (TupIsNull(slot))
{
*isNull = true;
return (Datum) 0;
}
tupdesc = slot->ttc_tupleDescriptor;
natts = slot->val->t_data->t_natts;
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, "GetAttributeByName: attribute %s not found", attname);
retval = heap_getattr(slot->val,
attrno,
tupdesc,
isNull);
if (*isNull)
return (Datum) 0;
return retval;
}
/*
* 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;
fcache->func.fn_expr = (Node *) fcache->xprstate.expr;
}
/*
* Evaluate arguments for a function.
*/
static ExprDoneCond
ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList,
ExprContext *econtext)
{
ExprDoneCond argIsDone;
int i;
List *arg;
argIsDone = ExprSingleResult; /* default assumption */
i = 0;
foreach(arg, argList)
{
ExprDoneCond thisArgIsDone;
fcinfo->arg[i] = ExecEvalExpr((ExprState *) lfirst(arg),
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)
elog(ERROR, "Functions and operators can take only 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;
/*
* 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
elog(ERROR, "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)
elog(ERROR, "Set-valued function called in context that cannot accept a set");
/*
* This loop handles the situation where we have both a set
* argument and a set-valued function. Once we have exhausted the
* function's value(s) for a particular argument value, we have to
* get the next argument value and start the function over again.
* We might have to do it more than once, if the function produces
* an empty result set for a particular input value.
*/
for (;;)
{
/*
* 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;
}
/*
* 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.
*
* 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;
}
/*
* 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;
FunctionCallInfoData fcinfo;
ReturnSetInfo rsinfo;
MemoryContext callerContext;
MemoryContext oldcontext;
TupleTableSlot *slot;
bool direct_function_call;
bool first_time = true;
bool returnsTuple = false;
/*
* 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)
elog(ERROR, "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);
/*
* 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;
/*
* If first time through, build tupdesc and tuplestore for
* result
*/
if (first_time)
{
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
if (funcrettype == RECORDOID ||
get_typtype(funcrettype) == 'c')
{
/*
* Composite type, so function should have returned a
* TupleTableSlot; use its descriptor
*/
slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull ||
!slot ||
!IsA(slot, TupleTableSlot) ||
!slot->ttc_tupleDescriptor)
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
tupdesc = CreateTupleDescCopy(slot->ttc_tupleDescriptor);
returnsTuple = true;
}
else
{
/*
* Scalar type, so make a single-column descriptor
*/
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
"column",
funcrettype,
-1,
0,
false);
}
tupstore = tuplestore_begin_heap(true, false, SortMem);
MemoryContextSwitchTo(oldcontext);
rsinfo.setResult = tupstore;
rsinfo.setDesc = tupdesc;
}
/*
* Store current resultset item.
*/
if (returnsTuple)
{
slot = (TupleTableSlot *) DatumGetPointer(result);
if (fcinfo.isnull ||
!slot ||
!IsA(slot, TupleTableSlot) ||
TupIsNull(slot))
elog(ERROR, "ExecMakeTableFunctionResult: Invalid result from function returning tuple");
tuple = slot->val;
}
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)
elog(ERROR, "ExecMakeTableFunctionResult: Materialize-mode protocol not followed");
/* Done evaluating the set result */
break;
}
else
elog(ERROR, "ExecMakeTableFunctionResult: unknown 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)
{
/*
* 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);
}
return ExecMakeFunctionResult(fcache, econtext, isNull, isDone);
}
/* ----------------------------------------------------------------
* ExecEvalOper
* ----------------------------------------------------------------
*/
static Datum
ExecEvalOper(FuncExprState *fcache,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
/*
* Initialize function cache if first time through
*/
if (fcache->func.fn_oid == InvalidOid)
{
OpExpr *op = (OpExpr *) fcache->xprstate.expr;
init_fcache(op->opfuncid, fcache, econtext->ecxt_per_query_memory);
}
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)
{
Datum result;
FunctionCallInfoData fcinfo;
ExprDoneCond argDone;
List *argList;
/*
* 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)
elog(ERROR, "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)
{
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;
/*
* 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)
elog(ERROR, "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)
{
ExprState *clause;
Datum expr_value;
clause = lfirst(notclause->args);
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)
{
List *clauses;
List *clause;
bool AnyNull;
Datum clause_value;
clauses = orExpr->args;
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)
{
clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
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)
{
List *clauses;
List *clause;
bool AnyNull;
Datum clause_value;
clauses = andExpr->args;
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)
{
clause_value = ExecEvalExpr((ExprState *) lfirst(clause),
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;
List *clause;
Datum clause_value;
clauses = caseExpr->args;
/*
* 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);
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)
{
return ExecEvalExpr(wclause->result,
econtext,
isNull,
isDone);
}
}
if (caseExpr->defresult)
{
return ExecEvalExpr(caseExpr->defresult,
econtext,
isNull,
isDone);
}
*isNull = true;
return (Datum) 0;
}
/* ----------------------------------------------------------------
* ExecEvalArray - ARRAY[] expressions
* ----------------------------------------------------------------
*/
static Datum
ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
bool *isNull)
{
ArrayExpr *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
ArrayType *result;
List *element;
Oid element_type = arrayExpr->element_typeid;
int ndims = arrayExpr->ndims;
int dims[MAXDIM];
int lbs[MAXDIM];
if (ndims == 1)
{
int nelems;
Datum *dvalues;
int i = 0;
nelems = 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)
elog(ERROR, "Arrays cannot have NULL elements");
}
/* 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
{
char *dat = NULL;
Size ndatabytes = 0;
int nbytes;
int outer_nelems = length(astate->elements);
int elem_ndims = 0;
int *elem_dims = NULL;
int *elem_lbs = NULL;
bool firstone = true;
int i;
if (ndims <= 0 || ndims > MAXDIM)
elog(ERROR, "Arrays cannot have more than %d dimensions", MAXDIM);
/* 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)
elog(ERROR, "Arrays cannot have NULL elements");
array = DatumGetArrayTypeP(arraydatum);
if (firstone)
{
/* Get sub-array details from first member */
elem_ndims = ARR_NDIM(array);
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))
elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching number of dimensions");
if (memcmp(elem_dims, ARR_DIMS(array),
elem_ndims * sizeof(int)) != 0)
elog(ERROR, "Multidimensional arrays must have array "
"expressions with matching dimensions");
if (memcmp(elem_lbs, ARR_LBOUND(array),
elem_ndims * sizeof(int)) != 0)
elog(ERROR, "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);
}
/* ----------------------------------------------------------------
* ExecEvalCoalesce
* ----------------------------------------------------------------
*/
static Datum
ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
bool *isNull)
{
List *arg;
/* 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 *fcache, ExprContext *econtext,
bool *isNull)
{
Datum result;
FunctionCallInfoData fcinfo;
ExprDoneCond argDone;
List *argList;
/*
* Initialize function cache if first time through
*/
if (fcache->func.fn_oid == InvalidOid)
{
NullIfExpr *op = (NullIfExpr *) 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)
elog(ERROR, "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, "ExecEvalNullTest: unexpected 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, "ExecEvalBooleanTest: unexpected 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;
List *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)
elog(ERROR, "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))
elog(ERROR, "ExecEvalCoerceToDomain: Domain %s constraint %s failed",
format_type_be(ctest->resulttype), con->name);
econtext->domainValue_datum = save_datum;
econtext->domainValue_isNull = save_isNull;
break;
}
default:
elog(ERROR, "ExecEvalCoerceToDomain: Constraint type unknown");
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(CoerceToDomainValue *conVal,
ExprContext *econtext, bool *isNull)
{
*isNull = econtext->domainValue_isNull;
return econtext->domainValue_datum;
}
/* ----------------------------------------------------------------
* ExecEvalFieldSelect
*
* Evaluate a FieldSelect node.
* ----------------------------------------------------------------
*/
static Datum
ExecEvalFieldSelect(GenericExprState *fstate,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
Datum result;
TupleTableSlot *resSlot;
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,
fselect->fieldnum,
resSlot->ttc_tupleDescriptor,
isNull);
return result;
}
/* ----------------------------------------------------------------
* ExecEvalExpr
*
* Recursively evaluate a targetlist or qualification expression.
*
* 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 elog()
* error will be reported. If the caller does pass an isDone pointer then
* *isDone is set to one of these three states:
* ExprSingleResult singleton result (not a set)
* ExprMultipleResult return value is one element of a set
* ExprEndResult there are no more elements in the set
* When ExprMultipleResult is returned, the caller should invoke
* ExecEvalExpr() repeatedly until ExprEndResult is returned. ExprEndResult
* is returned after the last real set element. For convenience isNull will
* always be set TRUE when ExprEndResult is returned, but this should not be
* taken as indicating a NULL element of the set. Note that these return
* conventions allow us to distinguish among a singleton NULL, a NULL element
* of a set, and an empty set.
*
* The caller should already have switched into the temporary memory
* context econtext->ecxt_per_tuple_memory. The convenience entry point
* ExecEvalExprSwitchContext() is provided for callers who don't prefer to
* do the switch in an outer loop. We do not do the switch here because
* it'd be a waste of cycles during recursive entries to ExecEvalExpr().
*
* This routine is an inner loop routine and must be as fast as possible.
* ----------------------------------------------------------------
*/
Datum
ExecEvalExpr(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;
if (isDone)
*isDone = ExprSingleResult;
/* Is this still necessary? Doubtful... */
if (expression == NULL)
{
*isNull = true;
return (Datum) 0;
}
/*
* here we dispatch the work to the appropriate type of function given
* the type of our expression.
*/
expr = expression->expr;
switch (nodeTag(expr))
{
case T_Var:
retDatum = ExecEvalVar((Var *) expr, econtext, isNull);
break;
case T_Const:
{
Const *con = (Const *) expr;
retDatum = con->constvalue;
*isNull = con->constisnull;
break;
}
case T_Param:
retDatum = ExecEvalParam((Param *) expr, econtext, isNull);
break;
case T_Aggref:
retDatum = ExecEvalAggref((AggrefExprState *) expression,
econtext,
isNull);
break;
case T_ArrayRef:
retDatum = ExecEvalArrayRef((ArrayRefExprState *) expression,
econtext,
isNull,
isDone);
break;
case T_FuncExpr:
retDatum = ExecEvalFunc((FuncExprState *) expression, econtext,
isNull, isDone);
break;
case T_OpExpr:
retDatum = ExecEvalOper((FuncExprState *) expression, econtext,
isNull, isDone);
break;
case T_DistinctExpr:
retDatum = ExecEvalDistinct((FuncExprState *) expression, econtext,
isNull);
break;
case T_ScalarArrayOpExpr:
retDatum = ExecEvalScalarArrayOp((ScalarArrayOpExprState *) expression,
econtext, isNull);
break;
case T_BoolExpr:
{
BoolExprState *state = (BoolExprState *) expression;
switch (((BoolExpr *) expr)->boolop)
{
case AND_EXPR:
retDatum = ExecEvalAnd(state, econtext, isNull);
break;
case OR_EXPR:
retDatum = ExecEvalOr(state, econtext, isNull);
break;
case NOT_EXPR:
retDatum = ExecEvalNot(state, econtext, isNull);
break;
default:
elog(ERROR, "ExecEvalExpr: unknown boolop %d",
((BoolExpr *) expr)->boolop);
retDatum = 0; /* keep compiler quiet */
break;
}
break;
}
case T_SubPlan:
retDatum = ExecSubPlan((SubPlanState *) expression,
econtext,
isNull);
break;
case T_FieldSelect:
retDatum = ExecEvalFieldSelect((GenericExprState *) expression,
econtext,
isNull,
isDone);
break;
case T_RelabelType:
retDatum = ExecEvalExpr(((GenericExprState *) expression)->arg,
econtext,
isNull,
isDone);
break;
case T_CaseExpr:
retDatum = ExecEvalCase((CaseExprState *) expression,
econtext,
isNull,
isDone);
break;
case T_ArrayExpr:
retDatum = ExecEvalArray((ArrayExprState *) expression,
econtext,
isNull);
break;
case T_CoalesceExpr:
retDatum = ExecEvalCoalesce((CoalesceExprState *) expression,
econtext,
isNull);
break;
case T_NullIfExpr:
retDatum = ExecEvalNullIf((FuncExprState *) expression,
econtext,
isNull);
break;
case T_NullTest:
retDatum = ExecEvalNullTest((GenericExprState *) expression,
econtext,
isNull,
isDone);
break;
case T_BooleanTest:
retDatum = ExecEvalBooleanTest((GenericExprState *) expression,
econtext,
isNull,
isDone);
break;
case T_CoerceToDomain:
retDatum = ExecEvalCoerceToDomain((CoerceToDomainState *) expression,
econtext,
isNull,
isDone);
break;
case T_CoerceToDomainValue:
retDatum = ExecEvalCoerceToDomainValue((CoerceToDomainValue *) expr,
econtext,
isNull);
break;
default:
elog(ERROR, "ExecEvalExpr: unknown expression type %d",
nodeTag(expression));
retDatum = 0; /* keep compiler quiet */
break;
}
return retDatum;
} /* ExecEvalExpr() */
/*
* Same as above, 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;
switch (nodeTag(node))
{
case T_Var:
case T_Const:
case T_Param:
case T_CoerceToDomainValue:
/* No special setup needed for these node types */
state = (ExprState *) makeNode(ExprState);
break;
case T_Aggref:
{
Aggref *aggref = (Aggref *) node;
AggrefExprState *astate = makeNode(AggrefExprState);
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 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;
}
break;
case T_ArrayRef:
{
ArrayRef *aref = (ArrayRef *) node;
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);
/* 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->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->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->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.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);
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);
if (!parent)
elog(ERROR, "ExecInitExpr: SubPlan not expected here");
/*
* 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;
GenericExprState *gstate = makeNode(GenericExprState);
gstate->arg = ExecInitExpr(fselect->arg, parent);
state = (ExprState *) gstate;
}
break;
case T_RelabelType:
{
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);
FastList outlist;
List *inlist;
FastListInit(&outlist);
foreach(inlist, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(inlist);
CaseWhenState *wstate = makeNode(CaseWhenState);
Assert(IsA(when, CaseWhen));
wstate->xprstate.expr = (Expr *) when;
wstate->expr = ExecInitExpr(when->expr, parent);
wstate->result = ExecInitExpr(when->result, parent);
FastAppend(&outlist, wstate);
}
cstate->args = FastListValue(&outlist);
/* caseexpr->arg should be null by now */
Assert(caseexpr->arg == NULL);
cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
state = (ExprState *) cstate;
}
break;
case T_ArrayExpr:
{
ArrayExpr *arrayexpr = (ArrayExpr *) node;
ArrayExprState *astate = makeNode(ArrayExprState);
FastList outlist;
List *inlist;
FastListInit(&outlist);
foreach(inlist, arrayexpr->elements)
{
Expr *e = (Expr *) lfirst(inlist);
ExprState *estate;
estate = ExecInitExpr(e, parent);
FastAppend(&outlist, estate);
}
astate->elements = FastListValue(&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_CoalesceExpr:
{
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExprState *cstate = makeNode(CoalesceExprState);
FastList outlist;
List *inlist;
FastListInit(&outlist);
foreach(inlist, coalesceexpr->args)
{
Expr *e = (Expr *) lfirst(inlist);
ExprState *estate;
estate = ExecInitExpr(e, parent);
FastAppend(&outlist, estate);
}
cstate->args = FastListValue(&outlist);
state = (ExprState *) cstate;
}
break;
case T_NullIfExpr:
{
NullIfExpr *nullifexpr = (NullIfExpr *) node;
FuncExprState *fstate = makeNode(FuncExprState);
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->arg = ExecInitExpr(ntest->arg, parent);
state = (ExprState *) gstate;
}
break;
case T_BooleanTest:
{
BooleanTest *btest = (BooleanTest *) node;
GenericExprState *gstate = makeNode(GenericExprState);
gstate->arg = ExecInitExpr(btest->arg, parent);
state = (ExprState *) gstate;
}
break;
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
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->arg = ExecInitExpr(tle->expr, parent);
state = (ExprState *) gstate;
}
break;
case T_List:
{
FastList outlist;
List *inlist;
FastListInit(&outlist);
foreach(inlist, (List *) node)
{
FastAppend(&outlist,
ExecInitExpr((Expr *) lfirst(inlist),
parent));
}
/* Don't fall through to the "common" code below */
return (ExprState *) FastListValue(&outlist);
}
default:
elog(ERROR, "ExecInitExpr: unknown expression type %d",
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, "ExecInitExpr: SubPlan not expected here");
/* 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;
List *qlist;
/*
* 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(qlist, qual)
{
ExprState *clause = (ExprState *) lfirst(qlist);
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 length(targetlist);
}
/*
* Number of items in a tlist, not including any resjunk items
*/
int
ExecCleanTargetListLength(List *targetlist)
{
int len = 0;
List *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;
List *tl;
bool isNull;
bool haveDoneSets;
static struct tupleDesc NullTupleDesc; /* we assume this inits to
* zeroes */
/*
* 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. We do, however,
* have to cope with the possibility that targettype is NULL ---
* heap_formtuple won't like that, so pass a dummy descriptor with
* natts = 0 to deal with it.
*/
if (targettype == NULL)
targettype = &NullTupleDesc;
/*
* 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)
elog(ERROR, "Set-valued function called in context that cannot accept a set");
if (itemIsDone[resind] == ExprMultipleResult)
{
/* we have undone sets in the tlist, set flag */
*isDone = ExprMultipleResult;
}
else
{
/* 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 (TupleTableSlot *) 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);
}