+ * 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)
+{
+ 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)
+ {
+ *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
+ {
+ 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)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions exceeds the maximum allowed, %d",
+ 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)
+ {
+ *isNull = true;
+ return (Datum) 0;
+ }
+
+ 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) ||
+ 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);
+}
+
+/* ----------------------------------------------------------------
+ * 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)
+ 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;
+ 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)
+ 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(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 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 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, "unrecognized boolop: %d",
+ (int) ((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, "unrecognized node type: %d",
+ (int) 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.