summaryrefslogtreecommitdiff
path: root/src/backend/executor/execQual.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/execQual.c')
-rw-r--r--src/backend/executor/execQual.c5313
1 files changed, 0 insertions, 5313 deletions
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 90bef6f01f0..e69de29bb2d 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -1,5313 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * execQual.c
- * Routines to evaluate qualification and targetlist expressions
- *
- * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
- * Portions Copyright (c) 1994, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * src/backend/executor/execQual.c
- *
- *-------------------------------------------------------------------------
- */
-/*
- * INTERFACE ROUTINES
- * ExecEvalExpr - (now a macro) evaluate an expression, return a datum
- * ExecEvalExprSwitchContext - same, but switch into eval memory context
- * ExecQual - return true/false if qualification is satisfied
- * ExecProject - form a new tuple by projecting the given tuple
- *
- * NOTES
- * The more heavily used ExecEvalExpr routines, such as ExecEvalScalarVar,
- * are hotspots. Making these faster will speed up the entire system.
- *
- * ExecProject() is used to make tuple projections. Rather then
- * trying to speed it up, the execution plan should be pre-processed
- * to facilitate attribute sharing between nodes wherever possible,
- * instead of doing needless copying. -cim 5/31/91
- *
- * During expression evaluation, we check_stack_depth only in
- * ExecMakeFunctionResultSet/ExecMakeFunctionResultNoSets rather than at
- * every single node. This is a compromise that trades off precision of
- * the stack limit setting to gain speed.
- */
-
-#include "postgres.h"
-
-#include "access/htup_details.h"
-#include "access/nbtree.h"
-#include "access/tupconvert.h"
-#include "catalog/objectaccess.h"
-#include "catalog/pg_type.h"
-#include "executor/execdebug.h"
-#include "executor/nodeSubplan.h"
-#include "funcapi.h"
-#include "miscadmin.h"
-#include "nodes/makefuncs.h"
-#include "nodes/nodeFuncs.h"
-#include "optimizer/planner.h"
-#include "parser/parse_coerce.h"
-#include "parser/parsetree.h"
-#include "pgstat.h"
-#include "utils/acl.h"
-#include "utils/builtins.h"
-#include "utils/date.h"
-#include "utils/lsyscache.h"
-#include "utils/memutils.h"
-#include "utils/timestamp.h"
-#include "utils/typcache.h"
-#include "utils/xml.h"
-
-
-/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
-static Datum ExecEvalAggref(AggrefExprState *aggref,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalWindowFunc(WindowFuncExprState *wfunc,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static void init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
- MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF);
-static void ShutdownFuncExpr(Datum arg);
-static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
- TupleDesc *cache_field, ExprContext *econtext);
-static void ShutdownTupleDescRef(Datum arg);
-static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
- List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
- ExprContext *econtext,
- Tuplestorestate *resultStore,
- TupleDesc resultDesc);
-static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
-static Datum ExecMakeFunctionResultNoSets(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalFunc(FuncExprState *fcache, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalOper(FuncExprState *fcache, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalDistinct(FuncExprState *fcache, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalScalarArrayOp(ScalarArrayOpExprState *sstate,
- ExprContext *econtext,
- bool *isNull);
-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 ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCase(CaseExprState *caseExpr, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCaseTestExpr(ExprState *exprstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalArray(ArrayExprState *astate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalRow(RowExprState *rstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalRowCompare(RowCompareExprState *rstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCoalesce(CoalesceExprState *coalesceExpr,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalMinMax(MinMaxExprState *minmaxExpr,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalSQLValueFunction(ExprState *svfExpr,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalNullIf(FuncExprState *nullIfExpr,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalNullTest(NullTestState *nstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalBooleanTest(GenericExprState *bstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCoerceToDomain(CoerceToDomainState *cstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalFieldStore(FieldStoreState *fstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalRelabelType(GenericExprState *exprstate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
- bool *isNull);
-static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
- ExprContext *econtext,
- bool *isNull);
-
-
-/* ----------------------------------------------------------------
- * ExecEvalExpr routines
- *
- * Recursively evaluate a targetlist or qualification expression.
- *
- * Each of the following routines having the signature
- * Datum ExecEvalFoo(ExprState *expression,
- * ExprContext *econtext,
- * bool *isNull);
- * is responsible for evaluating one type or subtype of ExprState node.
- * They are normally called via the ExecEvalExpr macro, which makes use of
- * the function pointer set up when the ExprState node was built by
- * ExecInitExpr. (In some cases, we change this pointer later to avoid
- * re-executing one-time overhead.)
- *
- * Note: for notational simplicity we declare these functions as taking the
- * specific type of ExprState that they work on. This requires casting when
- * assigning the function pointer in ExecInitExpr. Be careful that the
- * function signature is declared correctly, because the cast suppresses
- * automatic checking!
- *
- *
- * All these functions share this calling convention:
- *
- * Inputs:
- * expression: the expression state tree to evaluate
- * econtext: evaluation context information
- *
- * Outputs:
- * return value: Datum value of result
- * *isNull: set to TRUE if result is NULL (actual return value is
- * meaningless if so); set to FALSE if non-null result
- *
- * The caller should already have switched into the temporary memory
- * context econtext->ecxt_per_tuple_memory. The convenience entry point
- * ExecEvalExprSwitchContext() is provided for callers who don't prefer to
- * do the switch in an outer loop. We do not do the switch in these routines
- * because it'd be a waste of cycles during nested expression evaluation.
- * ----------------------------------------------------------------
- */
-
-
-/*----------
- * ExecEvalArrayRef
- *
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
- *
- * NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
- *----------
- */
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull)
-{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull);
-
- /*
- * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
- * assignment case, we'll cons up something below.
- */
- if (*isNull)
- {
- if (!isAssignment)
- return (Datum) NULL;
- }
-
- foreach(l, astate->refupperindexpr)
- {
- ExprState *eltstate = (ExprState *) lfirst(l);
-
- if (i >= MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
- i + 1, MAXDIM)));
-
- if (eltstate == NULL)
- {
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
- upperProvided[i++] = false;
- continue;
- }
- upperProvided[i] = true;
-
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull));
- /* If any index expr yields NULL, result is NULL or error */
- if (eisnull)
- {
- if (isAssignment)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
- *isNull = true;
- return (Datum) NULL;
- }
- }
-
- if (astate->reflowerindexpr != NIL)
- {
- foreach(l, astate->reflowerindexpr)
- {
- ExprState *eltstate = (ExprState *) lfirst(l);
-
- if (j >= MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
- j + 1, MAXDIM)));
-
- if (eltstate == NULL)
- {
- /* Slice bound is omitted, so use array's lower bound */
- lowerProvided[j++] = false;
- continue;
- }
- lowerProvided[j] = true;
-
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull));
- /* If any index expr yields NULL, result is NULL or error */
- if (eisnull)
- {
- if (isAssignment)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
- *isNull = true;
- return (Datum) NULL;
- }
- }
- /* this can't happen unless parser messed up */
- if (i != j)
- elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
- }
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- /* this is currently unreachable */
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
-
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- }
-
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
-
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
-
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
- }
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
-
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
-}
-
-/* ----------------------------------------------------------------
- * 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, "no aggregates in this expression context");
-
- *isNull = econtext->ecxt_aggnulls[aggref->aggno];
- return econtext->ecxt_aggvalues[aggref->aggno];
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalWindowFunc
- *
- * Returns a Datum whose value is the value of the precomputed
- * window function found in the given expression context.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWindowFunc(WindowFuncExprState *wfunc, ExprContext *econtext,
- bool *isNull)
-{
- if (econtext->ecxt_aggvalues == NULL) /* safety check */
- elog(ERROR, "no window functions in this expression context");
-
- *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
- return econtext->ecxt_aggvalues[wfunc->wfuncno];
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalScalarVar
- *
- * Returns a Datum whose value is the value of a scalar (not whole-row)
- * range variable with respect to given expression context.
- *
- * Note: ExecEvalScalarVar is executed only the first time through in a given
- * plan; it changes the ExprState's function pointer to pass control directly
- * to ExecEvalScalarVarFast after making one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- Var *variable = (Var *) exprstate->expr;
- TupleTableSlot *slot;
- AttrNumber attnum;
-
- /* Get the input slot and attribute number we want */
- switch (variable->varno)
- {
- case INNER_VAR: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- attnum = variable->varattno;
-
- /* This was checked by ExecInitExpr */
- Assert(attnum != InvalidAttrNumber);
-
- /*
- * If it's a user attribute, check validity (bogus system attnums will be
- * caught inside slot_getattr). What we have to check for here is the
- * possibility of an attribute having been changed in type since the plan
- * tree was created. Ideally the plan will get invalidated and not
- * re-used, but just in case, we keep these defenses. Fortunately it's
- * sufficient to check once on the first time through.
- *
- * Note: we allow a reference to a dropped attribute. slot_getattr will
- * force a NULL result in such cases.
- *
- * Note: ideally we'd check typmod as well as typid, but that seems
- * impractical at the moment: in many cases the tupdesc will have been
- * generated by ExecTypeFromTL(), and that can't guarantee to generate an
- * accurate typmod in all cases, because some expression node types don't
- * carry typmod.
- */
- if (attnum > 0)
- {
- TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
- Form_pg_attribute attr;
-
- if (attnum > slot_tupdesc->natts) /* should never happen */
- elog(ERROR, "attribute number %d exceeds number of columns %d",
- attnum, slot_tupdesc->natts);
-
- attr = slot_tupdesc->attrs[attnum - 1];
-
- /* can't check type if dropped, since atttypid is probably 0 */
- if (!attr->attisdropped)
- {
- if (variable->vartype != attr->atttypid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("attribute %d has wrong type", attnum),
- errdetail("Table has type %s, but query expects %s.",
- format_type_be(attr->atttypid),
- format_type_be(variable->vartype))));
- }
- }
-
- /* Skip the checking on future executions of node */
- exprstate->evalfunc = ExecEvalScalarVarFast;
-
- /* Fetch the value from the slot */
- return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalScalarVarFast
- *
- * Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- Var *variable = (Var *) exprstate->expr;
- TupleTableSlot *slot;
- AttrNumber attnum;
-
- /* Get the input slot and attribute number we want */
- switch (variable->varno)
- {
- case INNER_VAR: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- attnum = variable->varattno;
-
- /* Fetch the value from the slot */
- return slot_getattr(slot, attnum, isNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalWholeRowVar
- *
- * Returns a Datum whose value is the value of a whole-row range
- * variable with respect to given expression context.
- *
- * Note: ExecEvalWholeRowVar is executed only the first time through in a
- * given plan; it changes the ExprState's function pointer to pass control
- * directly to ExecEvalWholeRowFast or ExecEvalWholeRowSlow after making
- * one-time checks.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
- bool *isNull)
-{
- Var *variable = (Var *) wrvstate->xprstate.expr;
- TupleTableSlot *slot;
- TupleDesc output_tupdesc;
- MemoryContext oldcontext;
- bool needslow = false;
-
- /* This was checked by ExecInitExpr */
- Assert(variable->varattno == InvalidAttrNumber);
-
- /* Get the input slot we want */
- switch (variable->varno)
- {
- case INNER_VAR: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- /*
- * If the input tuple came from a subquery, it might contain "resjunk"
- * columns (such as GROUP BY or ORDER BY columns), which we don't want to
- * keep in the whole-row result. We can get rid of such columns by
- * passing the tuple through a JunkFilter --- but to make one, we have to
- * lay our hands on the subquery's targetlist. Fortunately, there are not
- * very many cases where this can happen, and we can identify all of them
- * by examining our parent PlanState. We assume this is not an issue in
- * standalone expressions that don't have parent plans. (Whole-row Vars
- * can occur in such expressions, but they will always be referencing
- * table rows.)
- */
- if (wrvstate->parent)
- {
- PlanState *subplan = NULL;
-
- switch (nodeTag(wrvstate->parent))
- {
- case T_SubqueryScanState:
- subplan = ((SubqueryScanState *) wrvstate->parent)->subplan;
- break;
- case T_CteScanState:
- subplan = ((CteScanState *) wrvstate->parent)->cteplanstate;
- break;
- default:
- break;
- }
-
- if (subplan)
- {
- bool junk_filter_needed = false;
- ListCell *tlist;
-
- /* Detect whether subplan tlist actually has any junk columns */
- foreach(tlist, subplan->plan->targetlist)
- {
- TargetEntry *tle = (TargetEntry *) lfirst(tlist);
-
- if (tle->resjunk)
- {
- junk_filter_needed = true;
- break;
- }
- }
-
- /* If so, build the junkfilter in the query memory context */
- if (junk_filter_needed)
- {
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- wrvstate->wrv_junkFilter =
- ExecInitJunkFilter(subplan->plan->targetlist,
- ExecGetResultType(subplan)->tdhasoid,
- ExecInitExtraTupleSlot(wrvstate->parent->state));
- MemoryContextSwitchTo(oldcontext);
- }
- }
- }
-
- /* Apply the junkfilter if any */
- if (wrvstate->wrv_junkFilter != NULL)
- slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
- /*
- * If the Var identifies a named composite type, we must check that the
- * actual tuple type is compatible with it.
- */
- if (variable->vartype != RECORDOID)
- {
- TupleDesc var_tupdesc;
- TupleDesc slot_tupdesc;
- int i;
-
- /*
- * We really only care about numbers of attributes and data types.
- * Also, we can ignore type mismatch on columns that are dropped in
- * the destination type, so long as (1) the physical storage matches
- * or (2) the actual column value is NULL. Case (1) is helpful in
- * some cases involving out-of-date cached plans, while case (2) is
- * expected behavior in situations such as an INSERT into a table with
- * dropped columns (the planner typically generates an INT4 NULL
- * regardless of the dropped column type). If we find a dropped
- * column and cannot verify that case (1) holds, we have to use
- * ExecEvalWholeRowSlow to check (2) for each row.
- */
- var_tupdesc = lookup_rowtype_tupdesc(variable->vartype, -1);
-
- slot_tupdesc = slot->tts_tupleDescriptor;
-
- if (var_tupdesc->natts != slot_tupdesc->natts)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail_plural("Table row contains %d attribute, but query expects %d.",
- "Table row contains %d attributes, but query expects %d.",
- slot_tupdesc->natts,
- slot_tupdesc->natts,
- var_tupdesc->natts)));
-
- for (i = 0; i < var_tupdesc->natts; i++)
- {
- Form_pg_attribute vattr = var_tupdesc->attrs[i];
- Form_pg_attribute sattr = slot_tupdesc->attrs[i];
-
- if (vattr->atttypid == sattr->atttypid)
- continue; /* no worries */
- if (!vattr->attisdropped)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Table has type %s at ordinal position %d, but query expects %s.",
- format_type_be(sattr->atttypid),
- i + 1,
- format_type_be(vattr->atttypid))));
-
- if (vattr->attlen != sattr->attlen ||
- vattr->attalign != sattr->attalign)
- needslow = true; /* need runtime check for null */
- }
-
- /*
- * Use the variable's declared rowtype as the descriptor for the
- * output values, modulo possibly assigning new column names below. In
- * particular, we *must* absorb any attisdropped markings.
- */
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- output_tupdesc = CreateTupleDescCopy(var_tupdesc);
- MemoryContextSwitchTo(oldcontext);
-
- ReleaseTupleDesc(var_tupdesc);
- }
- else
- {
- /*
- * In the RECORD case, we use the input slot's rowtype as the
- * descriptor for the output values, modulo possibly assigning new
- * column names below.
- */
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- output_tupdesc = CreateTupleDescCopy(slot->tts_tupleDescriptor);
- MemoryContextSwitchTo(oldcontext);
- }
-
- /*
- * Construct a tuple descriptor for the composite values we'll produce,
- * and make sure its record type is "blessed". The main reason to do this
- * is to be sure that operations such as row_to_json() will see the
- * desired column names when they look up the descriptor from the type
- * information embedded in the composite values.
- *
- * We already got the correct physical datatype info above, but now we
- * should try to find the source RTE and adopt its column aliases, in case
- * they are different from the original rowtype's names. For example, in
- * "SELECT foo(t) FROM tab t(x,y)", the first two columns in the composite
- * output should be named "x" and "y" regardless of tab's column names.
- *
- * If we can't locate the RTE, assume the column names we've got are OK.
- * (As of this writing, the only cases where we can't locate the RTE are
- * in execution of trigger WHEN clauses, and then the Var will have the
- * trigger's relation's rowtype, so its names are fine.) Also, if the
- * creator of the RTE didn't bother to fill in an eref field, assume our
- * column names are OK. (This happens in COPY, and perhaps other places.)
- */
- if (econtext->ecxt_estate &&
- variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
- {
- RangeTblEntry *rte = rt_fetch(variable->varno,
- econtext->ecxt_estate->es_range_table);
-
- if (rte->eref)
- ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
- }
-
- /* Bless the tupdesc if needed, and save it in the execution state */
- wrvstate->wrv_tupdesc = BlessTupleDesc(output_tupdesc);
-
- /* Skip all the above on future executions of node */
- if (needslow)
- wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowSlow;
- else
- wrvstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowFast;
-
- /* Fetch the value */
- return (*wrvstate->xprstate.evalfunc) ((ExprState *) wrvstate, econtext,
- isNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalWholeRowFast
- *
- * Returns a Datum for a whole-row variable.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowFast(WholeRowVarExprState *wrvstate, ExprContext *econtext,
- bool *isNull)
-{
- Var *variable = (Var *) wrvstate->xprstate.expr;
- TupleTableSlot *slot;
- HeapTupleHeader dtuple;
-
- *isNull = false;
-
- /* Get the input slot we want */
- switch (variable->varno)
- {
- case INNER_VAR: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- /* Apply the junkfilter if any */
- if (wrvstate->wrv_junkFilter != NULL)
- slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
- /*
- * Copy the slot tuple and make sure any toasted fields get detoasted.
- */
- dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
- /*
- * Label the datum with the composite type info we identified before.
- */
- HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
- HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
- return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalWholeRowSlow
- *
- * Returns a Datum for a whole-row variable, in the "slow" case where
- * we can't just copy the subplan's output.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
- bool *isNull)
-{
- Var *variable = (Var *) wrvstate->xprstate.expr;
- TupleTableSlot *slot;
- HeapTuple tuple;
- TupleDesc tupleDesc;
- TupleDesc var_tupdesc;
- HeapTupleHeader dtuple;
- int i;
-
- *isNull = false;
-
- /* Get the input slot we want */
- switch (variable->varno)
- {
- case INNER_VAR: /* get the tuple from the inner node */
- slot = econtext->ecxt_innertuple;
- break;
-
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
-
- /* Apply the junkfilter if any */
- if (wrvstate->wrv_junkFilter != NULL)
- slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
-
- tuple = ExecFetchSlotTuple(slot);
- tupleDesc = slot->tts_tupleDescriptor;
-
- /* wrv_tupdesc is a good enough representation of the Var's rowtype */
- Assert(variable->vartype != RECORDOID);
- var_tupdesc = wrvstate->wrv_tupdesc;
-
- /* Check to see if any dropped attributes are non-null */
- for (i = 0; i < var_tupdesc->natts; i++)
- {
- Form_pg_attribute vattr = var_tupdesc->attrs[i];
- Form_pg_attribute sattr = tupleDesc->attrs[i];
-
- if (!vattr->attisdropped)
- continue; /* already checked non-dropped cols */
- if (heap_attisnull(tuple, i + 1))
- continue; /* null is always okay */
- if (vattr->attlen != sattr->attlen ||
- vattr->attalign != sattr->attalign)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
- errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
- i + 1)));
- }
-
- /*
- * Copy the slot tuple and make sure any toasted fields get detoasted.
- */
- dtuple = DatumGetHeapTupleHeader(ExecFetchSlotTupleDatum(slot));
-
- /*
- * Label the datum with the composite type info we identified before.
- */
- HeapTupleHeaderSetTypeId(dtuple, wrvstate->wrv_tupdesc->tdtypeid);
- HeapTupleHeaderSetTypMod(dtuple, wrvstate->wrv_tupdesc->tdtypmod);
-
- return PointerGetDatum(dtuple);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalConst
- *
- * Returns the value of a constant.
- *
- * Note that for pass-by-ref datatypes, we return a pointer to the
- * actual constant node. This is one of the reasons why functions
- * must treat their input arguments as read-only.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- Const *con = (Const *) exprstate->expr;
-
- *isNull = con->constisnull;
- return con->constvalue;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalParamExec
- *
- * Returns the value of a PARAM_EXEC parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExec(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- Param *expression = (Param *) exprstate->expr;
- int thisParamId = expression->paramid;
- ParamExecData *prm;
-
- /*
- * PARAM_EXEC params (internal executor parameters) are stored in the
- * ecxt_param_exec_vals array, and can be accessed by array index.
- */
- 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;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalParamExtern
- *
- * Returns the value of a PARAM_EXTERN parameter.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalParamExtern(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- Param *expression = (Param *) exprstate->expr;
- int thisParamId = expression->paramid;
- ParamListInfo paramInfo = econtext->ecxt_param_list_info;
-
- /*
- * PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
- */
- if (paramInfo &&
- thisParamId > 0 && thisParamId <= paramInfo->numParams)
- {
- ParamExternData *prm = &paramInfo->params[thisParamId - 1];
-
- /* give hook a chance in case parameter is dynamic */
- if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
- (*paramInfo->paramFetch) (paramInfo, thisParamId);
-
- if (OidIsValid(prm->ptype))
- {
- /* safety check in case hook did something unexpected */
- if (prm->ptype != expression->paramtype)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
- thisParamId,
- format_type_be(prm->ptype),
- format_type_be(expression->paramtype))));
-
- *isNull = prm->isnull;
- return prm->value;
- }
- }
-
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("no value found for parameter %d", thisParamId)));
- return (Datum) 0; /* keep compiler quiet */
-}
-
-
-/* ----------------------------------------------------------------
- * ExecEvalOper / ExecEvalFunc support routines
- * ----------------------------------------------------------------
- */
-
-/*
- * GetAttributeByName
- * GetAttributeByNum
- *
- * These functions return the value of the requested attribute
- * out of the given tuple Datum.
- * C functions which take a tuple as an argument are expected
- * to use these. Ex: overpaid(EMP) might call GetAttributeByNum().
- * Note: these are actually rather slow because they do a typcache
- * lookup on each call.
- */
-Datum
-GetAttributeByNum(HeapTupleHeader tuple,
- AttrNumber attrno,
- bool *isNull)
-{
- Datum result;
- Oid tupType;
- int32 tupTypmod;
- TupleDesc tupDesc;
- HeapTupleData tmptup;
-
- if (!AttributeNumberIsValid(attrno))
- elog(ERROR, "invalid attribute number %d", attrno);
-
- if (isNull == NULL)
- elog(ERROR, "a NULL isNull pointer was passed");
-
- if (tuple == NULL)
- {
- /* Kinda bogus but compatible with old behavior... */
- *isNull = true;
- return (Datum) 0;
- }
-
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
- tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-
- /*
- * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all
- * the fields in the struct just in case user tries to inspect system
- * columns.
- */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- ItemPointerSetInvalid(&(tmptup.t_self));
- tmptup.t_tableOid = InvalidOid;
- tmptup.t_data = tuple;
-
- result = heap_getattr(&tmptup,
- attrno,
- tupDesc,
- isNull);
-
- ReleaseTupleDesc(tupDesc);
-
- return result;
-}
-
-Datum
-GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
-{
- AttrNumber attrno;
- Datum result;
- Oid tupType;
- int32 tupTypmod;
- TupleDesc tupDesc;
- HeapTupleData tmptup;
- int i;
-
- if (attname == NULL)
- elog(ERROR, "invalid attribute name");
-
- if (isNull == NULL)
- elog(ERROR, "a NULL isNull pointer was passed");
-
- if (tuple == NULL)
- {
- /* Kinda bogus but compatible with old behavior... */
- *isNull = true;
- return (Datum) 0;
- }
-
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
- tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-
- attrno = InvalidAttrNumber;
- for (i = 0; i < tupDesc->natts; i++)
- {
- if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
- {
- attrno = tupDesc->attrs[i]->attnum;
- break;
- }
- }
-
- if (attrno == InvalidAttrNumber)
- elog(ERROR, "attribute \"%s\" does not exist", attname);
-
- /*
- * heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all
- * the fields in the struct just in case user tries to inspect system
- * columns.
- */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- ItemPointerSetInvalid(&(tmptup.t_self));
- tmptup.t_tableOid = InvalidOid;
- tmptup.t_data = tuple;
-
- result = heap_getattr(&tmptup,
- attrno,
- tupDesc,
- isNull);
-
- ReleaseTupleDesc(tupDesc);
-
- return result;
-}
-
-/*
- * init_fcache - initialize a FuncExprState node during first use
- */
-static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
- MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
-{
- AclResult aclresult;
-
- /* Check permission to call function */
- aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
- InvokeFunctionExecuteHook(foid);
-
- /*
- * Safety check on nargs. Under normal circumstances this should never
- * fail, as parser should check sooner. But possibly it might fail if
- * server has been compiled with FUNC_MAX_ARGS smaller than some functions
- * declared in pg_proc?
- */
- if (list_length(fcache->args) > FUNC_MAX_ARGS)
- ereport(ERROR,
- (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
- errmsg_plural("cannot pass more than %d argument to a function",
- "cannot pass more than %d arguments to a function",
- FUNC_MAX_ARGS,
- FUNC_MAX_ARGS)));
-
- /* Set up the primary fmgr lookup information */
- fmgr_info_cxt(foid, &(fcache->func), fcacheCxt);
- fmgr_info_set_expr((Node *) fcache->xprstate.expr, &(fcache->func));
-
- /* Initialize the function call parameter struct as well */
- InitFunctionCallInfoData(fcache->fcinfo_data, &(fcache->func),
- list_length(fcache->args),
- input_collation, NULL, NULL);
-
- /* If function returns set, check if that's allowed by caller */
- if (fcache->func.fn_retset && !allowSRF)
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("set-valued function called in context that cannot accept a set")));
-
- /* Otherwise, ExecInitExpr should have marked the fcache correctly */
- Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
-
- /* If function returns set, prepare expected tuple descriptor */
- if (fcache->func.fn_retset && needDescForSRF)
- {
- TypeFuncClass functypclass;
- Oid funcrettype;
- TupleDesc tupdesc;
- MemoryContext oldcontext;
-
- functypclass = get_expr_result_type(fcache->func.fn_expr,
- &funcrettype,
- &tupdesc);
-
- /* Must save tupdesc in fcache's context */
- oldcontext = MemoryContextSwitchTo(fcacheCxt);
-
- if (functypclass == TYPEFUNC_COMPOSITE)
- {
- /* Composite data type, e.g. a table's row type */
- Assert(tupdesc);
- /* Must copy it out of typcache for safety */
- fcache->funcResultDesc = CreateTupleDescCopy(tupdesc);
- fcache->funcReturnsTuple = true;
- }
- else if (functypclass == TYPEFUNC_SCALAR)
- {
- /* Base data type, i.e. scalar */
- tupdesc = CreateTemplateTupleDesc(1, false);
- TupleDescInitEntry(tupdesc,
- (AttrNumber) 1,
- NULL,
- funcrettype,
- -1,
- 0);
- fcache->funcResultDesc = tupdesc;
- fcache->funcReturnsTuple = false;
- }
- else if (functypclass == TYPEFUNC_RECORD)
- {
- /* This will work if function doesn't need an expectedDesc */
- fcache->funcResultDesc = NULL;
- fcache->funcReturnsTuple = true;
- }
- else
- {
- /* Else, we will fail if function needs an expectedDesc */
- fcache->funcResultDesc = NULL;
- }
-
- MemoryContextSwitchTo(oldcontext);
- }
- else
- fcache->funcResultDesc = NULL;
-
- /* Initialize additional state */
- fcache->funcResultStore = NULL;
- fcache->funcResultSlot = NULL;
- fcache->shutdown_reg = false;
-}
-
-/*
- * callback function in case a FuncExpr returning a set needs to be shut down
- * before it has been run to completion
- */
-static void
-ShutdownFuncExpr(Datum arg)
-{
- FuncExprState *fcache = (FuncExprState *) DatumGetPointer(arg);
-
- /* If we have a slot, make sure it's let go of any tuplestore pointer */
- if (fcache->funcResultSlot)
- ExecClearTuple(fcache->funcResultSlot);
-
- /* Release any open tuplestore */
- if (fcache->funcResultStore)
- tuplestore_end(fcache->funcResultStore);
- fcache->funcResultStore = NULL;
-
- /* Clear any active set-argument state */
- fcache->setArgsValid = false;
-
- /* execUtils will deregister the callback... */
- fcache->shutdown_reg = false;
-}
-
-/*
- * get_cached_rowtype: utility function to lookup a rowtype tupdesc
- *
- * type_id, typmod: identity of the rowtype
- * cache_field: where to cache the TupleDesc pointer in expression state node
- * (field must be initialized to NULL)
- * econtext: expression context we are executing in
- *
- * NOTE: because the shutdown callback will be called during plan rescan,
- * must be prepared to re-do this during any node execution; cannot call
- * just once during expression initialization
- */
-static TupleDesc
-get_cached_rowtype(Oid type_id, int32 typmod,
- TupleDesc *cache_field, ExprContext *econtext)
-{
- TupleDesc tupDesc = *cache_field;
-
- /* Do lookup if no cached value or if requested type changed */
- if (tupDesc == NULL ||
- type_id != tupDesc->tdtypeid ||
- typmod != tupDesc->tdtypmod)
- {
- tupDesc = lookup_rowtype_tupdesc(type_id, typmod);
-
- if (*cache_field)
- {
- /* Release old tupdesc; but callback is already registered */
- ReleaseTupleDesc(*cache_field);
- }
- else
- {
- /* Need to register shutdown callback to release tupdesc */
- RegisterExprContextCallback(econtext,
- ShutdownTupleDescRef,
- PointerGetDatum(cache_field));
- }
- *cache_field = tupDesc;
- }
- return tupDesc;
-}
-
-/*
- * Callback function to release a tupdesc refcount at expression tree shutdown
- */
-static void
-ShutdownTupleDescRef(Datum arg)
-{
- TupleDesc *cache_field = (TupleDesc *) DatumGetPointer(arg);
-
- if (*cache_field)
- ReleaseTupleDesc(*cache_field);
- *cache_field = NULL;
-}
-
-/*
- * Evaluate arguments for a function.
- */
-static void
-ExecEvalFuncArgs(FunctionCallInfo fcinfo,
- List *argList,
- ExprContext *econtext)
-{
- int i;
- ListCell *arg;
-
- i = 0;
- foreach(arg, argList)
- {
- ExprState *argstate = (ExprState *) lfirst(arg);
-
- fcinfo->arg[i] = ExecEvalExpr(argstate,
- econtext,
- &fcinfo->argnull[i]);
- i++;
- }
-
- Assert(i == fcinfo->nargs);
-}
-
-/*
- * ExecPrepareTuplestoreResult
- *
- * Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
- * tuplestore function result. We must set up a funcResultSlot (unless
- * already done in a previous call cycle) and verify that the function
- * returned the expected tuple descriptor.
- */
-static void
-ExecPrepareTuplestoreResult(FuncExprState *fcache,
- ExprContext *econtext,
- Tuplestorestate *resultStore,
- TupleDesc resultDesc)
-{
- fcache->funcResultStore = resultStore;
-
- if (fcache->funcResultSlot == NULL)
- {
- /* Create a slot so we can read data out of the tuplestore */
- TupleDesc slotDesc;
- MemoryContext oldcontext;
-
- oldcontext = MemoryContextSwitchTo(fcache->func.fn_mcxt);
-
- /*
- * If we were not able to determine the result rowtype from context,
- * and the function didn't return a tupdesc, we have to fail.
- */
- if (fcache->funcResultDesc)
- slotDesc = fcache->funcResultDesc;
- else if (resultDesc)
- {
- /* don't assume resultDesc is long-lived */
- slotDesc = CreateTupleDescCopy(resultDesc);
- }
- else
- {
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("function returning setof record called in "
- "context that cannot accept type record")));
- slotDesc = NULL; /* keep compiler quiet */
- }
-
- fcache->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
- MemoryContextSwitchTo(oldcontext);
- }
-
- /*
- * If function provided a tupdesc, cross-check it. We only really need to
- * do this for functions returning RECORD, but might as well do it always.
- */
- if (resultDesc)
- {
- if (fcache->funcResultDesc)
- tupledesc_match(fcache->funcResultDesc, resultDesc);
-
- /*
- * If it is a dynamically-allocated TupleDesc, free it: it is
- * typically allocated in a per-query context, so we must avoid
- * leaking it across multiple usages.
- */
- if (resultDesc->tdrefcount == -1)
- FreeTupleDesc(resultDesc);
- }
-
- /* Register cleanup callback if we didn't already */
- if (!fcache->shutdown_reg)
- {
- RegisterExprContextCallback(econtext,
- ShutdownFuncExpr,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = true;
- }
-}
-
-/*
- * Check that function result tuple type (src_tupdesc) matches or can
- * be considered to match what the query expects (dst_tupdesc). If
- * they don't match, ereport.
- *
- * We really only care about number of attributes and data type.
- * Also, we can ignore type mismatch on columns that are dropped in the
- * destination type, so long as the physical storage matches. This is
- * helpful in some cases involving out-of-date cached plans.
- */
-static void
-tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
-{
- int i;
-
- if (dst_tupdesc->natts != src_tupdesc->natts)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("function return row and query-specified return row do not match"),
- errdetail_plural("Returned row contains %d attribute, but query expects %d.",
- "Returned row contains %d attributes, but query expects %d.",
- src_tupdesc->natts,
- src_tupdesc->natts, dst_tupdesc->natts)));
-
- for (i = 0; i < dst_tupdesc->natts; i++)
- {
- Form_pg_attribute dattr = dst_tupdesc->attrs[i];
- Form_pg_attribute sattr = src_tupdesc->attrs[i];
-
- if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
- continue; /* no worries */
- if (!dattr->attisdropped)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("function return row and query-specified return row do not match"),
- errdetail("Returned type %s at ordinal position %d, but query expects %s.",
- format_type_be(sattr->atttypid),
- i + 1,
- format_type_be(dattr->atttypid))));
-
- if (dattr->attlen != sattr->attlen ||
- dattr->attalign != sattr->attalign)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("function return row and query-specified return row do not match"),
- errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
- i + 1)));
- }
-}
-
-/*
- * ExecMakeFunctionResultSet
- *
- * Evaluate the arguments to a set-returning function and then call the
- * function itself. The argument expressions may not contain set-returning
- * functions (the planner is supposed to have separated evaluation for those).
- */
-Datum
-ExecMakeFunctionResultSet(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
-{
- List *arguments;
- Datum result;
- FunctionCallInfo fcinfo;
- PgStat_FunctionCallUsage fcusage;
- ReturnSetInfo rsinfo;
- bool callit;
- int i;
-
-restart:
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /*
- * Initialize function cache if first time through. The expression node
- * could be either a FuncExpr or an OpExpr.
- */
- if (fcache->func.fn_oid == InvalidOid)
- {
- if (IsA(fcache->xprstate.expr, FuncExpr))
- {
- FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
-
- init_fcache(func->funcid, func->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true, true);
- }
- else if (IsA(fcache->xprstate.expr, OpExpr))
- {
- OpExpr *op = (OpExpr *) fcache->xprstate.expr;
-
- init_fcache(op->opfuncid, op->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true, true);
- }
- else
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(fcache->xprstate.expr));
-
- /* shouldn't get here otherwise */
- Assert(fcache->func.fn_retset);
- }
-
- /*
- * If a previous call of the function returned a set result in the form of
- * a tuplestore, continue reading rows from the tuplestore until it's
- * empty.
- */
- if (fcache->funcResultStore)
- {
- if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
- fcache->funcResultSlot))
- {
- *isDone = ExprMultipleResult;
- if (fcache->funcReturnsTuple)
- {
- /* We must return the whole tuple as a Datum. */
- *isNull = false;
- return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
- }
- else
- {
- /* Extract the first column and return it as a scalar. */
- return slot_getattr(fcache->funcResultSlot, 1, isNull);
- }
- }
- /* Exhausted the tuplestore, so clean up */
- tuplestore_end(fcache->funcResultStore);
- fcache->funcResultStore = NULL;
- *isDone = ExprEndResult;
- *isNull = true;
- return (Datum) 0;
- }
-
- /*
- * 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.
- */
- fcinfo = &fcache->fcinfo_data;
- arguments = fcache->args;
- if (!fcache->setArgsValid)
- ExecEvalFuncArgs(fcinfo, arguments, econtext);
- else
- {
- /* Reset flag (we may set it again below) */
- fcache->setArgsValid = false;
- }
-
- /*
- * Now call the function, passing the evaluated parameter values.
- */
-
- /* Prepare a resultinfo node for communication. */
- fcinfo->resultinfo = (Node *) &rsinfo;
- rsinfo.type = T_ReturnSetInfo;
- rsinfo.econtext = econtext;
- rsinfo.expectedDesc = fcache->funcResultDesc;
- rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
- /* note we do not set SFRM_Materialize_Random or _Preferred */
- rsinfo.returnMode = SFRM_ValuePerCall;
- /* isDone is filled below */
- rsinfo.setResult = NULL;
- rsinfo.setDesc = NULL;
-
- /*
- * If function is strict, and there are any NULL arguments, skip calling
- * the function.
- */
- callit = true;
- if (fcache->func.fn_strict)
- {
- for (i = 0; i < fcinfo->nargs; i++)
- {
- if (fcinfo->argnull[i])
- {
- callit = false;
- break;
- }
- }
- }
-
- if (callit)
- {
- pgstat_init_function_usage(fcinfo, &fcusage);
-
- fcinfo->isnull = false;
- rsinfo.isDone = ExprSingleResult;
- result = FunctionCallInvoke(fcinfo);
- *isNull = fcinfo->isnull;
- *isDone = rsinfo.isDone;
-
- pgstat_end_function_usage(&fcusage,
- rsinfo.isDone != ExprMultipleResult);
- }
- else
- {
- /* for a strict SRF, result for NULL is an empty set */
- result = (Datum) 0;
- *isNull = true;
- *isDone = ExprEndResult;
- }
-
- /* Which protocol does function want to use? */
- if (rsinfo.returnMode == SFRM_ValuePerCall)
- {
- if (*isDone != ExprEndResult)
- {
- /*
- * Save the current argument values to re-use on the next call.
- */
- if (*isDone == ExprMultipleResult)
- {
- fcache->setArgsValid = true;
- /* Register cleanup callback if we didn't already */
- if (!fcache->shutdown_reg)
- {
- RegisterExprContextCallback(econtext,
- ShutdownFuncExpr,
- PointerGetDatum(fcache));
- fcache->shutdown_reg = true;
- }
- }
- }
- }
- else if (rsinfo.returnMode == SFRM_Materialize)
- {
- /* check we're on the same page as the function author */
- if (rsinfo.isDone != ExprSingleResult)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
- errmsg("table-function protocol for materialize mode was not followed")));
- if (rsinfo.setResult != NULL)
- {
- /* prepare to return values from the tuplestore */
- ExecPrepareTuplestoreResult(fcache, econtext,
- rsinfo.setResult,
- rsinfo.setDesc);
- /* loop back to top to start returning from tuplestore */
- goto restart;
- }
- /* if setResult was left null, treat it as empty set */
- *isDone = ExprEndResult;
- *isNull = true;
- result = (Datum) 0;
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
- errmsg("unrecognized table-function returnMode: %d",
- (int) rsinfo.returnMode)));
-
- return result;
-}
-
-/*
- * ExecMakeFunctionResultNoSets
- *
- * Evaluate a function or operator node with a non-set-returning function.
- * Assumes init_fcache() already done. Hand-tuned for speed.
- */
-static Datum
-ExecMakeFunctionResultNoSets(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull)
-{
- ListCell *arg;
- Datum result;
- FunctionCallInfo fcinfo;
- PgStat_FunctionCallUsage fcusage;
- int i;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- /* inlined, simplified version of ExecEvalFuncArgs */
- fcinfo = &fcache->fcinfo_data;
- i = 0;
- foreach(arg, fcache->args)
- {
- ExprState *argstate = (ExprState *) lfirst(arg);
-
- fcinfo->arg[i] = ExecEvalExpr(argstate,
- econtext,
- &fcinfo->argnull[i]);
- i++;
- }
-
- /*
- * If function is strict, and there are any NULL arguments, skip calling
- * the function and return NULL.
- */
- if (fcache->func.fn_strict)
- {
- while (--i >= 0)
- {
- if (fcinfo->argnull[i])
- {
- *isNull = true;
- return (Datum) 0;
- }
- }
- }
-
- pgstat_init_function_usage(fcinfo, &fcusage);
-
- fcinfo->isnull = false;
- result = FunctionCallInvoke(fcinfo);
- *isNull = fcinfo->isnull;
-
- pgstat_end_function_usage(&fcusage, true);
-
- return result;
-}
-
-
-/*
- * ExecMakeTableFunctionResult
- *
- * Evaluate a table function, producing a materialized result in a Tuplestore
- * object.
- */
-Tuplestorestate *
-ExecMakeTableFunctionResult(ExprState *funcexpr,
- ExprContext *econtext,
- MemoryContext argContext,
- TupleDesc expectedDesc,
- bool randomAccess)
-{
- Tuplestorestate *tupstore = NULL;
- TupleDesc tupdesc = NULL;
- Oid funcrettype;
- bool returnsTuple;
- bool returnsSet = false;
- FunctionCallInfoData fcinfo;
- PgStat_FunctionCallUsage fcusage;
- ReturnSetInfo rsinfo;
- HeapTupleData tmptup;
- MemoryContext callerContext;
- MemoryContext oldcontext;
- bool direct_function_call;
- bool first_time = true;
-
- callerContext = CurrentMemoryContext;
-
- funcrettype = exprType((Node *) funcexpr->expr);
-
- returnsTuple = type_is_rowtype(funcrettype);
-
- /*
- * 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.
- */
- rsinfo.type = T_ReturnSetInfo;
- rsinfo.econtext = econtext;
- rsinfo.expectedDesc = expectedDesc;
- rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
- if (randomAccess)
- rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
- rsinfo.returnMode = SFRM_ValuePerCall;
- /* isDone is filled below */
- rsinfo.setResult = NULL;
- rsinfo.setDesc = NULL;
-
- /*
- * 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;
-
- /*
- * This path is similar to ExecMakeFunctionResultSet.
- */
- 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, func->inputcollid, fcache,
- econtext->ecxt_per_query_memory, true, false);
- }
- returnsSet = fcache->func.fn_retset;
- InitFunctionCallInfoData(fcinfo, &(fcache->func),
- list_length(fcache->args),
- fcache->fcinfo_data.fncollation,
- NULL, (Node *) &rsinfo);
-
- /*
- * Evaluate the function's argument list.
- *
- * We can't do this in the per-tuple context: the argument values
- * would disappear when we reset that context in the inner loop. And
- * the caller's CurrentMemoryContext is typically a query-lifespan
- * context, so we don't want to leak memory there. We require the
- * caller to pass a separate memory context that can be used for this,
- * and can be reset each time through to avoid bloat.
- */
- MemoryContextReset(argContext);
- oldcontext = MemoryContextSwitchTo(argContext);
- ExecEvalFuncArgs(&fcinfo, fcache->args, econtext);
- MemoryContextSwitchTo(oldcontext);
-
- /*
- * If function is strict, and there are any NULL arguments, skip
- * calling the function and act like it returned NULL (or an empty
- * set, in the returns-set case).
- */
- if (fcache->func.fn_strict)
- {
- int i;
-
- for (i = 0; i < fcinfo.nargs; i++)
- {
- if (fcinfo.argnull[i])
- goto no_function_result;
- }
- }
- }
- else
- {
- /* Treat funcexpr as a generic expression */
- direct_function_call = false;
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- }
-
- /*
- * Switch to short-lived context for calling the function or expression.
- */
- 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;
-
- CHECK_FOR_INTERRUPTS();
-
- /*
- * 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)
- {
- pgstat_init_function_usage(&fcinfo, &fcusage);
-
- fcinfo.isnull = false;
- rsinfo.isDone = ExprSingleResult;
- result = FunctionCallInvoke(&fcinfo);
-
- pgstat_end_function_usage(&fcusage,
- rsinfo.isDone != ExprMultipleResult);
- }
- else
- {
- result = ExecEvalExpr(funcexpr, econtext, &fcinfo.isnull);
- rsinfo.isDone = ExprSingleResult;
- }
-
- /* Which protocol does function want to use? */
- if (rsinfo.returnMode == SFRM_ValuePerCall)
- {
- /*
- * Check for end of result set.
- */
- if (rsinfo.isDone == ExprEndResult)
- break;
-
- /*
- * If first time through, build tuplestore for result. For a
- * scalar function result type, also make a suitable tupdesc.
- */
- if (first_time)
- {
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
- rsinfo.setResult = tupstore;
- if (!returnsTuple)
- {
- tupdesc = CreateTemplateTupleDesc(1, false);
- TupleDescInitEntry(tupdesc,
- (AttrNumber) 1,
- "column",
- funcrettype,
- -1,
- 0);
- rsinfo.setDesc = tupdesc;
- }
- MemoryContextSwitchTo(oldcontext);
- }
-
- /*
- * Store current resultset item.
- */
- if (returnsTuple)
- {
- if (!fcinfo.isnull)
- {
- HeapTupleHeader td = DatumGetHeapTupleHeader(result);
-
- if (tupdesc == NULL)
- {
- /*
- * This is the first non-NULL result from the
- * function. Use the type info embedded in the
- * rowtype Datum to look up the needed tupdesc. Make
- * a copy for the query.
- */
- oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
- HeapTupleHeaderGetTypMod(td));
- rsinfo.setDesc = tupdesc;
- MemoryContextSwitchTo(oldcontext);
- }
- else
- {
- /*
- * Verify all later returned rows have same subtype;
- * necessary in case the type is RECORD.
- */
- if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
- HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("rows returned by function are not all of the same row type")));
- }
-
- /*
- * tuplestore_puttuple needs a HeapTuple not a bare
- * HeapTupleHeader, but it doesn't need all the fields.
- */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
- tmptup.t_data = td;
-
- tuplestore_puttuple(tupstore, &tmptup);
- }
- else
- {
- /*
- * NULL result from a tuple-returning function; expand it
- * to a row of all nulls. We rely on the expectedDesc to
- * form such rows. (Note: this would be problematic if
- * tuplestore_putvalues saved the tdtypeid/tdtypmod from
- * the provided descriptor, since that might not match
- * what we get from the function itself. But it doesn't.)
- */
- int natts = expectedDesc->natts;
- bool *nullflags;
-
- nullflags = (bool *) palloc(natts * sizeof(bool));
- memset(nullflags, true, natts * sizeof(bool));
- tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
- }
- }
- else
- {
- /* Scalar-type case: just store the function result */
- tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
- }
-
- /*
- * Are we done?
- */
- if (rsinfo.isDone != ExprMultipleResult)
- break;
- }
- else if (rsinfo.returnMode == SFRM_Materialize)
- {
- /* check we're on the same page as the function author */
- if (!first_time || rsinfo.isDone != ExprSingleResult)
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
- errmsg("table-function protocol for materialize mode was not followed")));
- /* Done evaluating the set result */
- break;
- }
- else
- ereport(ERROR,
- (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
- errmsg("unrecognized table-function returnMode: %d",
- (int) rsinfo.returnMode)));
-
- first_time = false;
- }
-
-no_function_result:
-
- /*
- * If we got nothing from the function (ie, an empty-set or NULL result),
- * we have to create the tuplestore to return, and if it's a
- * non-set-returning function then insert a single all-nulls row. As
- * above, we depend on the expectedDesc to manufacture the dummy row.
- */
- if (rsinfo.setResult == NULL)
- {
- MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
- tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
- rsinfo.setResult = tupstore;
- if (!returnsSet)
- {
- int natts = expectedDesc->natts;
- bool *nullflags;
-
- MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- nullflags = (bool *) palloc(natts * sizeof(bool));
- memset(nullflags, true, natts * sizeof(bool));
- tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
- }
- }
-
- /*
- * If function provided a tupdesc, cross-check it. We only really need to
- * do this for functions returning RECORD, but might as well do it always.
- */
- if (rsinfo.setDesc)
- {
- tupledesc_match(expectedDesc, rsinfo.setDesc);
-
- /*
- * If it is a dynamically-allocated TupleDesc, free it: it is
- * typically allocated in a per-query context, so we must avoid
- * leaking it across multiple usages.
- */
- if (rsinfo.setDesc->tdrefcount == -1)
- FreeTupleDesc(rsinfo.setDesc);
- }
-
- MemoryContextSwitchTo(callerContext);
-
- /* All done, pass back the tuplestore */
- 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)
-{
- /* This is called only the first time through */
- FuncExpr *func = (FuncExpr *) fcache->xprstate.expr;
-
- /* Initialize function lookup info */
- init_fcache(func->funcid, func->inputcollid, fcache,
- econtext->ecxt_per_query_memory, false, false);
-
- /* Change the evalfunc pointer to save a few cycles in additional calls */
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
- return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalOper
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalOper(FuncExprState *fcache,
- ExprContext *econtext,
- bool *isNull)
-{
- /* This is called only the first time through */
- OpExpr *op = (OpExpr *) fcache->xprstate.expr;
-
- /* Initialize function lookup info */
- init_fcache(op->opfuncid, op->inputcollid, fcache,
- econtext->ecxt_per_query_memory, false, false);
-
- /* Change the evalfunc pointer to save a few cycles in additional calls */
- fcache->xprstate.evalfunc = (ExprStateEvalFunc) ExecMakeFunctionResultNoSets;
- return ExecMakeFunctionResultNoSets(fcache, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- * 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;
- FunctionCallInfo fcinfo;
-
- /* Set non-null as default */
- *isNull = false;
-
- /*
- * Initialize function cache if first time through
- */
- if (fcache->func.fn_oid == InvalidOid)
- {
- DistinctExpr *op = (DistinctExpr *) fcache->xprstate.expr;
-
- init_fcache(op->opfuncid, op->inputcollid, fcache,
- econtext->ecxt_per_query_memory, false, false);
- }
-
- /*
- * Evaluate arguments
- */
- fcinfo = &fcache->fcinfo_data;
- ExecEvalFuncArgs(fcinfo, fcache->args, econtext);
- 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;
- FunctionCallInfo fcinfo;
- int i;
- int16 typlen;
- bool typbyval;
- char typalign;
- char *s;
- bits8 *bitmap;
- int bitmask;
-
- /* Set non-null as default */
- *isNull = false;
-
- /*
- * Initialize function cache if first time through
- */
- if (sstate->fxprstate.func.fn_oid == InvalidOid)
- {
- init_fcache(opexpr->opfuncid, opexpr->inputcollid, &sstate->fxprstate,
- econtext->ecxt_per_query_memory, false, false);
- }
-
- /*
- * Evaluate arguments
- */
- fcinfo = &sstate->fxprstate.fcinfo_data;
- ExecEvalFuncArgs(fcinfo, sstate->fxprstate.args, econtext);
- 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; no
- * point in iterating the loop.
- */
- 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);
- bitmap = ARR_NULLBITMAP(arr);
- bitmask = 1;
-
- for (i = 0; i < nitems; i++)
- {
- Datum elt;
- Datum thisresult;
-
- /* Get array element, checking for NULL */
- if (bitmap && (*bitmap & bitmask) == 0)
- {
- fcinfo->arg[1] = (Datum) 0;
- fcinfo->argnull[1] = true;
- }
- else
- {
- elt = fetch_att(s, typbyval, typlen);
- s = att_addlength_pointer(s, typlen, s);
- s = (char *) att_align_nominal(s, typalign);
- fcinfo->arg[1] = elt;
- fcinfo->argnull[1] = false;
- }
-
- /* Call comparison function */
- if (fcinfo->argnull[1] && sstate->fxprstate.func.fn_strict)
- {
- fcinfo->isnull = true;
- thisresult = (Datum) 0;
- }
- else
- {
- 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 */
- }
- }
-
- /* advance bitmap pointer if any */
- if (bitmap)
- {
- bitmask <<= 1;
- if (bitmask == 0x100)
- {
- bitmap++;
- bitmask = 1;
- }
- }
- }
-
- *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 = linitial(notclause->args);
- Datum expr_value;
-
- expr_value = ExecEvalExpr(clause, econtext, isNull);
-
- /*
- * 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 = orExpr->args;
- ListCell *clause;
- bool AnyNull;
-
- AnyNull = false;
-
- /*
- * If any of the clauses is TRUE, the OR result is TRUE regardless of the
- * states of the rest of the clauses, so we can stop evaluating and return
- * TRUE immediately. If none are TRUE and one or more is NULL, we return
- * NULL; otherwise we return FALSE. This makes sense when you interpret
- * NULL as "don't know": if we have a TRUE then the OR is TRUE even if we
- * aren't sure about some of the other inputs. If all the known inputs are
- * FALSE, but we have one or more "don't knows", then we have to report
- * that we "don't know" what the OR's result should be --- perhaps one of
- * the "don't knows" would have been TRUE if we'd known its value. Only
- * when all the inputs are known to be FALSE can we state confidently that
- * the OR's result is FALSE.
- */
- foreach(clause, clauses)
- {
- ExprState *clausestate = (ExprState *) lfirst(clause);
- Datum clause_value;
-
- clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
- /*
- * 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 = andExpr->args;
- ListCell *clause;
- bool AnyNull;
-
- AnyNull = false;
-
- /*
- * If any of the clauses is FALSE, the AND result is FALSE regardless of
- * the states of the rest of the clauses, so we can stop evaluating and
- * return FALSE immediately. If none are FALSE and one or more is NULL,
- * we return NULL; otherwise we return TRUE. This makes sense when you
- * interpret NULL as "don't know", using the same sort of reasoning as for
- * OR, above.
- */
-
- foreach(clause, clauses)
- {
- ExprState *clausestate = (ExprState *) lfirst(clause);
- Datum clause_value;
-
- clause_value = ExecEvalExpr(clausestate, econtext, isNull);
-
- /*
- * 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);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalConvertRowtype
- *
- * Evaluate a rowtype coercion operation. This may require
- * rearranging field positions.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalConvertRowtype(ConvertRowtypeExprState *cstate,
- ExprContext *econtext,
- bool *isNull)
-{
- ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) cstate->xprstate.expr;
- HeapTuple result;
- Datum tupDatum;
- HeapTupleHeader tuple;
- HeapTupleData tmptup;
-
- tupDatum = ExecEvalExpr(cstate->arg, econtext, isNull);
-
- /* this test covers the isDone exception too: */
- if (*isNull)
- return tupDatum;
-
- tuple = DatumGetHeapTupleHeader(tupDatum);
-
- /* Lookup tupdescs if first time through or after rescan */
- if (cstate->indesc == NULL)
- {
- get_cached_rowtype(exprType((Node *) convert->arg), -1,
- &cstate->indesc, econtext);
- cstate->initialized = false;
- }
- if (cstate->outdesc == NULL)
- {
- get_cached_rowtype(convert->resulttype, -1,
- &cstate->outdesc, econtext);
- cstate->initialized = false;
- }
-
- /*
- * We used to be able to assert that incoming tuples are marked with
- * exactly the rowtype of cstate->indesc. However, now that
- * ExecEvalWholeRowVar might change the tuples' marking to plain RECORD
- * due to inserting aliases, we can only make this weak test:
- */
- Assert(HeapTupleHeaderGetTypeId(tuple) == cstate->indesc->tdtypeid ||
- HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
-
- /* if first time through, initialize conversion map */
- if (!cstate->initialized)
- {
- MemoryContext old_cxt;
-
- /* allocate map in long-lived memory context */
- old_cxt = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
-
- /* prepare map from old to new attribute numbers */
- cstate->map = convert_tuples_by_name(cstate->indesc,
- cstate->outdesc,
- gettext_noop("could not convert row type"));
- cstate->initialized = true;
-
- MemoryContextSwitchTo(old_cxt);
- }
-
- /*
- * No-op if no conversion needed (not clear this can happen here).
- */
- if (cstate->map == NULL)
- return tupDatum;
-
- /*
- * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
- */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- tmptup.t_data = tuple;
-
- result = do_convert_tuple(&tmptup, cstate->map);
-
- return HeapTupleGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- * 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)
-{
- List *clauses = caseExpr->args;
- ListCell *clause;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * If there's a test expression, we have to evaluate it and save the value
- * where the CaseTestExpr placeholders can find it. We must save and
- * restore prior setting of econtext's caseValue fields, in case this node
- * is itself within a larger CASE. Furthermore, don't assign to the
- * econtext fields until after returning from evaluation of the test
- * expression. We used to pass &econtext->caseValue_isNull to the
- * recursive call, but that leads to aliasing that variable within said
- * call, which can (and did) produce bugs when the test expression itself
- * contains a CASE.
- *
- * If there's no test expression, we don't actually need to save and
- * restore these fields; but it's less code to just do so unconditionally.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (caseExpr->arg)
- {
- Datum arg_value;
- bool arg_isNull;
-
- arg_value = ExecEvalExpr(caseExpr->arg,
- econtext,
- &arg_isNull);
- /* Since caseValue_datum may be read multiple times, force to R/O */
- econtext->caseValue_datum =
- MakeExpandedObjectReadOnly(arg_value,
- arg_isNull,
- caseExpr->argtyplen);
- econtext->caseValue_isNull = arg_isNull;
- }
-
- /*
- * we evaluate each of the WHEN clauses in turn, as soon as one is true we
- * return the corresponding result. If none are true then we return the
- * value of the default clause, or NULL if there is none.
- */
- foreach(clause, clauses)
- {
- CaseWhenState *wclause = lfirst(clause);
- Datum clause_value;
- bool clause_isNull;
-
- clause_value = ExecEvalExpr(wclause->expr,
- econtext,
- &clause_isNull);
-
- /*
- * 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) && !clause_isNull)
- {
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
- return ExecEvalExpr(wclause->result,
- econtext,
- isNull);
- }
- }
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- if (caseExpr->defresult)
- {
- return ExecEvalExpr(caseExpr->defresult,
- econtext,
- isNull);
- }
-
- *isNull = true;
- return (Datum) 0;
-}
-
-/*
- * ExecEvalCaseTestExpr
- *
- * Return the value stored by CASE.
- */
-static Datum
-ExecEvalCaseTestExpr(ExprState *exprstate,
- ExprContext *econtext,
- bool *isNull)
-{
- *isNull = econtext->caseValue_isNull;
- return econtext->caseValue_datum;
-}
-
-/*
- * ExecEvalGroupingFuncExpr
- *
- * Return a bitmask with a bit for each (unevaluated) argument expression
- * (rightmost arg is least significant bit).
- *
- * A bit is set if the corresponding expression is NOT part of the set of
- * grouping expressions in the current grouping set.
- */
-static Datum
-ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
- ExprContext *econtext,
- bool *isNull)
-{
- int result = 0;
- int attnum = 0;
- Bitmapset *grouped_cols = gstate->aggstate->grouped_cols;
- ListCell *lc;
-
- *isNull = false;
-
- foreach(lc, (gstate->clauses))
- {
- attnum = lfirst_int(lc);
-
- result = result << 1;
-
- if (!bms_is_member(attnum, grouped_cols))
- result = result | 1;
- }
-
- return (Datum) result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalArray - ARRAY[] expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArray(ArrayExprState *astate, ExprContext *econtext,
- bool *isNull)
-{
- ArrayExpr *arrayExpr = (ArrayExpr *) astate->xprstate.expr;
- ArrayType *result;
- ListCell *element;
- Oid element_type = arrayExpr->element_typeid;
- int ndims = 0;
- int dims[MAXDIM];
- int lbs[MAXDIM];
-
- /* Set non-null as default */
- *isNull = false;
-
- if (!arrayExpr->multidims)
- {
- /* Elements are presumably of scalar type */
- int nelems;
- Datum *dvalues;
- bool *dnulls;
- int i = 0;
-
- ndims = 1;
- nelems = list_length(astate->elements);
-
- /* Shouldn't happen here, but if length is 0, return empty array */
- if (nelems == 0)
- return PointerGetDatum(construct_empty_array(element_type));
-
- dvalues = (Datum *) palloc(nelems * sizeof(Datum));
- dnulls = (bool *) palloc(nelems * sizeof(bool));
-
- /* loop through and build array of datums */
- foreach(element, astate->elements)
- {
- ExprState *e = (ExprState *) lfirst(element);
-
- dvalues[i] = ExecEvalExpr(e, econtext, &dnulls[i]);
- i++;
- }
-
- /* setup for 1-D array of the given length */
- dims[0] = nelems;
- lbs[0] = 1;
-
- result = construct_md_array(dvalues, dnulls, ndims, dims, lbs,
- element_type,
- astate->elemlength,
- astate->elembyval,
- astate->elemalign);
- }
- else
- {
- /* Must be nested array expressions */
- int nbytes = 0;
- int nitems = 0;
- int outer_nelems = 0;
- int elem_ndims = 0;
- int *elem_dims = NULL;
- int *elem_lbs = NULL;
- bool firstone = true;
- bool havenulls = false;
- bool haveempty = false;
- char **subdata;
- bits8 **subbitmaps;
- int *subbytes;
- int *subnitems;
- int i;
- int32 dataoffset;
- char *dat;
- int iitem;
-
- i = list_length(astate->elements);
- subdata = (char **) palloc(i * sizeof(char *));
- subbitmaps = (bits8 **) palloc(i * sizeof(bits8 *));
- subbytes = (int *) palloc(i * sizeof(int));
- subnitems = (int *) palloc(i * sizeof(int));
-
- /* 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 this_ndims;
-
- arraydatum = ExecEvalExpr(e, econtext, &eisnull);
- /* temporarily ignore null subarrays */
- if (eisnull)
- {
- haveempty = true;
- continue;
- }
-
- array = DatumGetArrayTypeP(arraydatum);
-
- /* run-time double-check on element type */
- if (element_type != ARR_ELEMTYPE(array))
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot merge incompatible arrays"),
- errdetail("Array with element type %s cannot be "
- "included in ARRAY construct with element type %s.",
- format_type_be(ARR_ELEMTYPE(array)),
- format_type_be(element_type))));
-
- this_ndims = ARR_NDIM(array);
- /* temporarily ignore zero-dimensional subarrays */
- if (this_ndims <= 0)
- {
- haveempty = true;
- continue;
- }
-
- if (firstone)
- {
- /* Get sub-array details from first member */
- elem_ndims = this_ndims;
- ndims = elem_ndims + 1;
- if (ndims <= 0 || ndims > MAXDIM)
- ereport(ERROR,
- (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds " \
- "the maximum allowed (%d)", ndims, MAXDIM)));
-
- elem_dims = (int *) palloc(elem_ndims * sizeof(int));
- memcpy(elem_dims, ARR_DIMS(array), elem_ndims * sizeof(int));
- elem_lbs = (int *) palloc(elem_ndims * sizeof(int));
- memcpy(elem_lbs, ARR_LBOUND(array), elem_ndims * sizeof(int));
-
- firstone = false;
- }
- else
- {
- /* Check other sub-arrays are compatible */
- if (elem_ndims != this_ndims ||
- 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")));
- }
-
- subdata[outer_nelems] = ARR_DATA_PTR(array);
- subbitmaps[outer_nelems] = ARR_NULLBITMAP(array);
- subbytes[outer_nelems] = ARR_SIZE(array) - ARR_DATA_OFFSET(array);
- nbytes += subbytes[outer_nelems];
- subnitems[outer_nelems] = ArrayGetNItems(this_ndims,
- ARR_DIMS(array));
- nitems += subnitems[outer_nelems];
- havenulls |= ARR_HASNULL(array);
- outer_nelems++;
- }
-
- /*
- * If all items were null or empty arrays, return an empty array;
- * otherwise, if some were and some weren't, raise error. (Note: we
- * must special-case this somehow to avoid trying to generate a 1-D
- * array formed from empty arrays. It's not ideal...)
- */
- if (haveempty)
- {
- if (ndims == 0) /* didn't find any nonempty array */
- return PointerGetDatum(construct_empty_array(element_type));
- ereport(ERROR,
- (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
- errmsg("multidimensional arrays must have array "
- "expressions with matching dimensions")));
- }
-
- /* 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];
- }
-
- if (havenulls)
- {
- dataoffset = ARR_OVERHEAD_WITHNULLS(ndims, nitems);
- nbytes += dataoffset;
- }
- else
- {
- dataoffset = 0; /* marker for no null bitmap */
- nbytes += ARR_OVERHEAD_NONULLS(ndims);
- }
-
- result = (ArrayType *) palloc(nbytes);
- SET_VARSIZE(result, nbytes);
- result->ndim = ndims;
- result->dataoffset = dataoffset;
- result->elemtype = element_type;
- memcpy(ARR_DIMS(result), dims, ndims * sizeof(int));
- memcpy(ARR_LBOUND(result), lbs, ndims * sizeof(int));
-
- dat = ARR_DATA_PTR(result);
- iitem = 0;
- for (i = 0; i < outer_nelems; i++)
- {
- memcpy(dat, subdata[i], subbytes[i]);
- dat += subbytes[i];
- if (havenulls)
- array_bitmap_copy(ARR_NULLBITMAP(result), iitem,
- subbitmaps[i], 0,
- subnitems[i]);
- iitem += subnitems[i];
- }
- }
-
- return PointerGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalRow - ROW() expressions
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRow(RowExprState *rstate,
- ExprContext *econtext,
- bool *isNull)
-{
- HeapTuple tuple;
- Datum *values;
- bool *isnull;
- int natts;
- ListCell *arg;
- int i;
-
- /* Set non-null as default */
- *isNull = false;
-
- /* Allocate workspace */
- natts = rstate->tupdesc->natts;
- values = (Datum *) palloc0(natts * sizeof(Datum));
- isnull = (bool *) palloc(natts * sizeof(bool));
-
- /* preset to nulls in case rowtype has some later-added columns */
- memset(isnull, true, natts * sizeof(bool));
-
- /* Evaluate field values */
- i = 0;
- foreach(arg, rstate->args)
- {
- ExprState *e = (ExprState *) lfirst(arg);
-
- values[i] = ExecEvalExpr(e, econtext, &isnull[i]);
- i++;
- }
-
- tuple = heap_form_tuple(rstate->tupdesc, values, isnull);
-
- pfree(values);
- pfree(isnull);
-
- return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalRowCompare - ROW() comparison-op ROW()
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRowCompare(RowCompareExprState *rstate,
- ExprContext *econtext,
- bool *isNull)
-{
- bool result;
- RowCompareType rctype = ((RowCompareExpr *) rstate->xprstate.expr)->rctype;
- int32 cmpresult = 0;
- ListCell *l;
- ListCell *r;
- int i;
-
- *isNull = true; /* until we get a result */
-
- i = 0;
- forboth(l, rstate->largs, r, rstate->rargs)
- {
- ExprState *le = (ExprState *) lfirst(l);
- ExprState *re = (ExprState *) lfirst(r);
- FunctionCallInfoData locfcinfo;
-
- InitFunctionCallInfoData(locfcinfo, &(rstate->funcs[i]), 2,
- rstate->collations[i],
- NULL, NULL);
- locfcinfo.arg[0] = ExecEvalExpr(le, econtext,
- &locfcinfo.argnull[0]);
- locfcinfo.arg[1] = ExecEvalExpr(re, econtext,
- &locfcinfo.argnull[1]);
- if (rstate->funcs[i].fn_strict &&
- (locfcinfo.argnull[0] || locfcinfo.argnull[1]))
- return (Datum) 0; /* force NULL result */
- locfcinfo.isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
- if (locfcinfo.isnull)
- return (Datum) 0; /* force NULL result */
- if (cmpresult != 0)
- break; /* no need to compare remaining columns */
- i++;
- }
-
- switch (rctype)
- {
- /* EQ and NE cases aren't allowed here */
- case ROWCOMPARE_LT:
- result = (cmpresult < 0);
- break;
- case ROWCOMPARE_LE:
- result = (cmpresult <= 0);
- break;
- case ROWCOMPARE_GE:
- result = (cmpresult >= 0);
- break;
- case ROWCOMPARE_GT:
- result = (cmpresult > 0);
- break;
- default:
- elog(ERROR, "unrecognized RowCompareType: %d", (int) rctype);
- result = 0; /* keep compiler quiet */
- break;
- }
-
- *isNull = false;
- return BoolGetDatum(result);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalCoalesce
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoalesce(CoalesceExprState *coalesceExpr, ExprContext *econtext,
- bool *isNull)
-{
- ListCell *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);
- if (!*isNull)
- return value;
- }
-
- /* Else return NULL */
- *isNull = true;
- return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalMinMax
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalMinMax(MinMaxExprState *minmaxExpr, ExprContext *econtext,
- bool *isNull)
-{
- Datum result = (Datum) 0;
- MinMaxExpr *minmax = (MinMaxExpr *) minmaxExpr->xprstate.expr;
- Oid collation = minmax->inputcollid;
- MinMaxOp op = minmax->op;
- FunctionCallInfoData locfcinfo;
- ListCell *arg;
-
- *isNull = true; /* until we get a result */
-
- InitFunctionCallInfoData(locfcinfo, &minmaxExpr->cfunc, 2,
- collation, NULL, NULL);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
-
- foreach(arg, minmaxExpr->args)
- {
- ExprState *e = (ExprState *) lfirst(arg);
- Datum value;
- bool valueIsNull;
- int32 cmpresult;
-
- value = ExecEvalExpr(e, econtext, &valueIsNull);
- if (valueIsNull)
- continue; /* ignore NULL inputs */
-
- if (*isNull)
- {
- /* first nonnull input, adopt value */
- result = value;
- *isNull = false;
- }
- else
- {
- /* apply comparison function */
- locfcinfo.arg[0] = result;
- locfcinfo.arg[1] = value;
- locfcinfo.isnull = false;
- cmpresult = DatumGetInt32(FunctionCallInvoke(&locfcinfo));
- if (locfcinfo.isnull) /* probably should not happen */
- continue;
- if (cmpresult > 0 && op == IS_LEAST)
- result = value;
- else if (cmpresult < 0 && op == IS_GREATEST)
- result = value;
- }
- }
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalSQLValueFunction
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalSQLValueFunction(ExprState *svfExpr,
- ExprContext *econtext,
- bool *isNull)
-{
- Datum result = (Datum) 0;
- SQLValueFunction *svf = (SQLValueFunction *) svfExpr->expr;
- FunctionCallInfoData fcinfo;
-
- *isNull = false;
-
- /*
- * Note: current_schema() can return NULL. current_user() etc currently
- * cannot, but might as well code those cases the same way for safety.
- */
- switch (svf->op)
- {
- case SVFOP_CURRENT_DATE:
- result = DateADTGetDatum(GetSQLCurrentDate());
- break;
- case SVFOP_CURRENT_TIME:
- case SVFOP_CURRENT_TIME_N:
- result = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
- break;
- case SVFOP_CURRENT_TIMESTAMP:
- case SVFOP_CURRENT_TIMESTAMP_N:
- result = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
- break;
- case SVFOP_LOCALTIME:
- case SVFOP_LOCALTIME_N:
- result = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
- break;
- case SVFOP_LOCALTIMESTAMP:
- case SVFOP_LOCALTIMESTAMP_N:
- result = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
- break;
- case SVFOP_CURRENT_ROLE:
- case SVFOP_CURRENT_USER:
- case SVFOP_USER:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- result = current_user(&fcinfo);
- *isNull = fcinfo.isnull;
- break;
- case SVFOP_SESSION_USER:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- result = session_user(&fcinfo);
- *isNull = fcinfo.isnull;
- break;
- case SVFOP_CURRENT_CATALOG:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- result = current_database(&fcinfo);
- *isNull = fcinfo.isnull;
- break;
- case SVFOP_CURRENT_SCHEMA:
- InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
- result = current_schema(&fcinfo);
- *isNull = fcinfo.isnull;
- break;
- }
-
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalXml
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalXml(XmlExprState *xmlExpr, ExprContext *econtext,
- bool *isNull)
-{
- XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
- Datum value;
- bool isnull;
- ListCell *arg;
- ListCell *narg;
-
- *isNull = true; /* until we get a result */
-
- switch (xexpr->op)
- {
- case IS_XMLCONCAT:
- {
- List *values = NIL;
-
- foreach(arg, xmlExpr->args)
- {
- ExprState *e = (ExprState *) lfirst(arg);
-
- value = ExecEvalExpr(e, econtext, &isnull);
- if (!isnull)
- values = lappend(values, DatumGetPointer(value));
- }
-
- if (list_length(values) > 0)
- {
- *isNull = false;
- return PointerGetDatum(xmlconcat(values));
- }
- else
- return (Datum) 0;
- }
- break;
-
- case IS_XMLFOREST:
- {
- StringInfoData buf;
-
- initStringInfo(&buf);
- forboth(arg, xmlExpr->named_args, narg, xexpr->arg_names)
- {
- ExprState *e = (ExprState *) lfirst(arg);
- char *argname = strVal(lfirst(narg));
-
- value = ExecEvalExpr(e, econtext, &isnull);
- if (!isnull)
- {
- appendStringInfo(&buf, "<%s>%s</%s>",
- argname,
- map_sql_value_to_xml_value(value, exprType((Node *) e->expr), true),
- argname);
- *isNull = false;
- }
- }
-
- if (*isNull)
- {
- pfree(buf.data);
- return (Datum) 0;
- }
- else
- {
- text *result;
-
- result = cstring_to_text_with_len(buf.data, buf.len);
- pfree(buf.data);
-
- return PointerGetDatum(result);
- }
- }
- break;
-
- case IS_XMLELEMENT:
- *isNull = false;
- return PointerGetDatum(xmlelement(xmlExpr, econtext));
- break;
-
- case IS_XMLPARSE:
- {
- ExprState *e;
- text *data;
- bool preserve_whitespace;
-
- /* arguments are known to be text, bool */
- Assert(list_length(xmlExpr->args) == 2);
-
- e = (ExprState *) linitial(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- return (Datum) 0;
- data = DatumGetTextPP(value);
-
- e = (ExprState *) lsecond(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull) /* probably can't happen */
- return (Datum) 0;
- preserve_whitespace = DatumGetBool(value);
-
- *isNull = false;
-
- return PointerGetDatum(xmlparse(data,
- xexpr->xmloption,
- preserve_whitespace));
- }
- break;
-
- case IS_XMLPI:
- {
- ExprState *e;
- text *arg;
-
- /* optional argument is known to be text */
- Assert(list_length(xmlExpr->args) <= 1);
-
- if (xmlExpr->args)
- {
- e = (ExprState *) linitial(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- arg = NULL;
- else
- arg = DatumGetTextPP(value);
- }
- else
- {
- arg = NULL;
- isnull = false;
- }
-
- return PointerGetDatum(xmlpi(xexpr->name, arg, isnull, isNull));
- }
- break;
-
- case IS_XMLROOT:
- {
- ExprState *e;
- xmltype *data;
- text *version;
- int standalone;
-
- /* arguments are known to be xml, text, int */
- Assert(list_length(xmlExpr->args) == 3);
-
- e = (ExprState *) linitial(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- return (Datum) 0;
- data = DatumGetXmlP(value);
-
- e = (ExprState *) lsecond(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- version = NULL;
- else
- version = DatumGetTextPP(value);
-
- e = (ExprState *) lthird(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- standalone = DatumGetInt32(value);
-
- *isNull = false;
-
- return PointerGetDatum(xmlroot(data,
- version,
- standalone));
- }
- break;
-
- case IS_XMLSERIALIZE:
- {
- ExprState *e;
-
- /* argument type is known to be xml */
- Assert(list_length(xmlExpr->args) == 1);
-
- e = (ExprState *) linitial(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- return (Datum) 0;
-
- *isNull = false;
-
- return PointerGetDatum(xmltotext_with_xmloption(DatumGetXmlP(value), xexpr->xmloption));
- }
- break;
-
- case IS_DOCUMENT:
- {
- ExprState *e;
-
- /* optional argument is known to be xml */
- Assert(list_length(xmlExpr->args) == 1);
-
- e = (ExprState *) linitial(xmlExpr->args);
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
- return (Datum) 0;
- else
- {
- *isNull = false;
- return BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
- }
- }
- break;
- }
-
- elog(ERROR, "unrecognized XML operation");
- return (Datum) 0;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalNullIf
- *
- * Note that this is *always* derived from the equals operator,
- * but since we need special processing of the arguments
- * we can not simply reuse ExecEvalOper() or ExecEvalFunc().
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalNullIf(FuncExprState *nullIfExpr,
- ExprContext *econtext,
- bool *isNull)
-{
- Datum result;
- FunctionCallInfo fcinfo;
-
- /*
- * Initialize function cache if first time through
- */
- if (nullIfExpr->func.fn_oid == InvalidOid)
- {
- NullIfExpr *op = (NullIfExpr *) nullIfExpr->xprstate.expr;
-
- init_fcache(op->opfuncid, op->inputcollid, nullIfExpr,
- econtext->ecxt_per_query_memory, false, false);
- }
-
- /*
- * Evaluate arguments
- */
- fcinfo = &nullIfExpr->fcinfo_data;
- ExecEvalFuncArgs(fcinfo, nullIfExpr->args, econtext);
- 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(NullTestState *nstate,
- ExprContext *econtext,
- bool *isNull)
-{
- NullTest *ntest = (NullTest *) nstate->xprstate.expr;
- Datum result;
-
- result = ExecEvalExpr(nstate->arg, econtext, isNull);
-
- if (ntest->argisrow && !(*isNull))
- {
- /*
- * The SQL standard defines IS [NOT] NULL for a non-null rowtype
- * argument as:
- *
- * "R IS NULL" is true if every field is the null value.
- *
- * "R IS NOT NULL" is true if no field is the null value.
- *
- * This definition is (apparently intentionally) not recursive; so our
- * tests on the fields are primitive attisnull tests, not recursive
- * checks to see if they are all-nulls or no-nulls rowtypes.
- *
- * The standard does not consider the possibility of zero-field rows,
- * but here we consider them to vacuously satisfy both predicates.
- */
- HeapTupleHeader tuple;
- Oid tupType;
- int32 tupTypmod;
- TupleDesc tupDesc;
- HeapTupleData tmptup;
- int att;
-
- tuple = DatumGetHeapTupleHeader(result);
-
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
- /* Lookup tupdesc if first time through or if type changes */
- tupDesc = get_cached_rowtype(tupType, tupTypmod,
- &nstate->argdesc, econtext);
-
- /*
- * heap_attisnull needs a HeapTuple not a bare HeapTupleHeader.
- */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- tmptup.t_data = tuple;
-
- for (att = 1; att <= tupDesc->natts; att++)
- {
- /* ignore dropped columns */
- if (tupDesc->attrs[att - 1]->attisdropped)
- continue;
- if (heap_attisnull(&tmptup, att))
- {
- /* null field disproves IS NOT NULL */
- if (ntest->nulltesttype == IS_NOT_NULL)
- return BoolGetDatum(false);
- }
- else
- {
- /* non-null field disproves IS NULL */
- if (ntest->nulltesttype == IS_NULL)
- return BoolGetDatum(false);
- }
- }
-
- return BoolGetDatum(true);
- }
- else
- {
- /* Simple scalar-argument case, or a null rowtype datum */
- 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)
-{
- BooleanTest *btest = (BooleanTest *) bstate->xprstate.expr;
- Datum result;
-
- result = ExecEvalExpr(bstate->arg, econtext, isNull);
-
- 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)
-{
- CoerceToDomain *ctest = (CoerceToDomain *) cstate->xprstate.expr;
- Datum result;
- ListCell *l;
-
- result = ExecEvalExpr(cstate->arg, econtext, isNull);
-
- /* Make sure we have up-to-date constraints */
- UpdateDomainConstraintRef(cstate->constraint_ref);
-
- foreach(l, cstate->constraint_ref->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)),
- errdatatype(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.
- *
- * Also, if we are working with a read-write expanded
- * datum, be sure that what we pass to CHECK expressions
- * is a read-only pointer; else called functions might
- * modify or even delete the expanded object.
- */
- save_datum = econtext->domainValue_datum;
- save_isNull = econtext->domainValue_isNull;
-
- econtext->domainValue_datum =
- MakeExpandedObjectReadOnly(result, *isNull,
- cstate->constraint_ref->tcache->typlen);
- econtext->domainValue_isNull = *isNull;
-
- conResult = ExecEvalExpr(con->check_expr, econtext,
- &conIsNull);
-
- 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),
- errdomainconstraint(ctest->resulttype,
- con->name)));
- econtext->domainValue_datum = save_datum;
- econtext->domainValue_isNull = save_isNull;
-
- break;
- }
- default:
- elog(ERROR, "unrecognized constraint type: %d",
- (int) con->constrainttype);
- break;
- }
- }
-
- /* If all has gone well (constraints did not fail) return the datum */
- return result;
-}
-
-/*
- * ExecEvalCoerceToDomainValue
- *
- * Return the value stored by CoerceToDomain.
- */
-static Datum
-ExecEvalCoerceToDomainValue(ExprState *exprstate,
- ExprContext *econtext,
- bool *isNull)
-{
- *isNull = econtext->domainValue_isNull;
- return econtext->domainValue_datum;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalFieldSelect
- *
- * Evaluate a FieldSelect node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldSelect(FieldSelectState *fstate,
- ExprContext *econtext,
- bool *isNull)
-{
- FieldSelect *fselect = (FieldSelect *) fstate->xprstate.expr;
- AttrNumber fieldnum = fselect->fieldnum;
- Datum result;
- Datum tupDatum;
- HeapTupleHeader tuple;
- Oid tupType;
- int32 tupTypmod;
- TupleDesc tupDesc;
- Form_pg_attribute attr;
- HeapTupleData tmptup;
-
- tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
- if (*isNull)
- return tupDatum;
-
- tuple = DatumGetHeapTupleHeader(tupDatum);
-
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
-
- /* Lookup tupdesc if first time through or if type changes */
- tupDesc = get_cached_rowtype(tupType, tupTypmod,
- &fstate->argdesc, econtext);
-
- /*
- * Find field's attr record. Note we don't support system columns here: a
- * datum tuple doesn't have valid values for most of the interesting
- * system columns anyway.
- */
- if (fieldnum <= 0) /* should never happen */
- elog(ERROR, "unsupported reference to system column %d in FieldSelect",
- fieldnum);
- if (fieldnum > tupDesc->natts) /* should never happen */
- elog(ERROR, "attribute number %d exceeds number of columns %d",
- fieldnum, tupDesc->natts);
- attr = tupDesc->attrs[fieldnum - 1];
-
- /* Check for dropped column, and force a NULL result if so */
- if (attr->attisdropped)
- {
- *isNull = true;
- return (Datum) 0;
- }
-
- /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
- /* As in ExecEvalScalarVar, we should but can't check typmod */
- if (fselect->resulttype != attr->atttypid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("attribute %d has wrong type", fieldnum),
- errdetail("Table has type %s, but query expects %s.",
- format_type_be(attr->atttypid),
- format_type_be(fselect->resulttype))));
-
- /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
- tmptup.t_data = tuple;
-
- result = heap_getattr(&tmptup,
- fieldnum,
- tupDesc,
- isNull);
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalFieldStore
- *
- * Evaluate a FieldStore node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalFieldStore(FieldStoreState *fstate,
- ExprContext *econtext,
- bool *isNull)
-{
- FieldStore *fstore = (FieldStore *) fstate->xprstate.expr;
- HeapTuple tuple;
- Datum tupDatum;
- TupleDesc tupDesc;
- Datum *values;
- bool *isnull;
- Datum save_datum;
- bool save_isNull;
- ListCell *l1,
- *l2;
-
- tupDatum = ExecEvalExpr(fstate->arg, econtext, isNull);
-
- /* Lookup tupdesc if first time through or after rescan */
- tupDesc = get_cached_rowtype(fstore->resulttype, -1,
- &fstate->argdesc, econtext);
-
- /* Allocate workspace */
- values = (Datum *) palloc(tupDesc->natts * sizeof(Datum));
- isnull = (bool *) palloc(tupDesc->natts * sizeof(bool));
-
- if (!*isNull)
- {
- /*
- * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
- * set all the fields in the struct just in case.
- */
- HeapTupleHeader tuphdr;
- HeapTupleData tmptup;
-
- tuphdr = DatumGetHeapTupleHeader(tupDatum);
- tmptup.t_len = HeapTupleHeaderGetDatumLength(tuphdr);
- ItemPointerSetInvalid(&(tmptup.t_self));
- tmptup.t_tableOid = InvalidOid;
- tmptup.t_data = tuphdr;
-
- heap_deform_tuple(&tmptup, tupDesc, values, isnull);
- }
- else
- {
- /* Convert null input tuple into an all-nulls row */
- memset(isnull, true, tupDesc->natts * sizeof(bool));
- }
-
- /* Result is never null */
- *isNull = false;
-
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- forboth(l1, fstate->newvals, l2, fstore->fieldnums)
- {
- ExprState *newval = (ExprState *) lfirst(l1);
- AttrNumber fieldnum = lfirst_int(l2);
-
- Assert(fieldnum > 0 && fieldnum <= tupDesc->natts);
-
- /*
- * Use the CaseTestExpr mechanism to pass down the old value of the
- * field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
- * It's safe to reuse the CASE mechanism because there cannot be a
- * CASE between here and where the value would be needed, and a field
- * assignment can't be within a CASE either. (So saving and restoring
- * the caseValue is just paranoia, but let's do it anyway.)
- */
- econtext->caseValue_datum = values[fieldnum - 1];
- econtext->caseValue_isNull = isnull[fieldnum - 1];
-
- values[fieldnum - 1] = ExecEvalExpr(newval,
- econtext,
- &isnull[fieldnum - 1]);
- }
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- tuple = heap_form_tuple(tupDesc, values, isnull);
-
- pfree(values);
- pfree(isnull);
-
- return HeapTupleGetDatum(tuple);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalRelabelType
- *
- * Evaluate a RelabelType node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalRelabelType(GenericExprState *exprstate,
- ExprContext *econtext,
- bool *isNull)
-{
- return ExecEvalExpr(exprstate->arg, econtext, isNull);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalCoerceViaIO
- *
- * Evaluate a CoerceViaIO node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCoerceViaIO(CoerceViaIOState *iostate,
- ExprContext *econtext,
- bool *isNull)
-{
- Datum result;
- Datum inputval;
- char *string;
-
- inputval = ExecEvalExpr(iostate->arg, econtext, isNull);
-
- if (*isNull)
- string = NULL; /* output functions are not called on nulls */
- else
- string = OutputFunctionCall(&iostate->outfunc, inputval);
-
- result = InputFunctionCall(&iostate->infunc,
- string,
- iostate->intypioparam,
- -1);
-
- /* The input function cannot change the null/not-null status */
- return result;
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalArrayCoerceExpr
- *
- * Evaluate an ArrayCoerceExpr node.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalArrayCoerceExpr(ArrayCoerceExprState *astate,
- ExprContext *econtext,
- bool *isNull)
-{
- ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) astate->xprstate.expr;
- Datum result;
- FunctionCallInfoData locfcinfo;
-
- result = ExecEvalExpr(astate->arg, econtext, isNull);
-
- if (*isNull)
- return result; /* nothing to do */
-
- /*
- * If it's binary-compatible, modify the element type in the array header,
- * but otherwise leave the array as we received it.
- */
- if (!OidIsValid(acoerce->elemfuncid))
- {
- /* Detoast input array if necessary, and copy in any case */
- ArrayType *array = DatumGetArrayTypePCopy(result);
-
- ARR_ELEMTYPE(array) = astate->resultelemtype;
- PG_RETURN_ARRAYTYPE_P(array);
- }
-
- /* Initialize function cache if first time through */
- if (astate->elemfunc.fn_oid == InvalidOid)
- {
- AclResult aclresult;
-
- /* Check permission to call function */
- aclresult = pg_proc_aclcheck(acoerce->elemfuncid, GetUserId(),
- ACL_EXECUTE);
- if (aclresult != ACLCHECK_OK)
- aclcheck_error(aclresult, ACL_KIND_PROC,
- get_func_name(acoerce->elemfuncid));
- InvokeFunctionExecuteHook(acoerce->elemfuncid);
-
- /* Set up the primary fmgr lookup information */
- fmgr_info_cxt(acoerce->elemfuncid, &(astate->elemfunc),
- econtext->ecxt_per_query_memory);
- fmgr_info_set_expr((Node *) acoerce, &(astate->elemfunc));
- }
-
- /*
- * Use array_map to apply the function to each array element.
- *
- * We pass on the desttypmod and isExplicit flags whether or not the
- * function wants them.
- *
- * Note: coercion functions are assumed to not use collation.
- */
- InitFunctionCallInfoData(locfcinfo, &(astate->elemfunc), 3,
- InvalidOid, NULL, NULL);
- locfcinfo.arg[0] = result;
- locfcinfo.arg[1] = Int32GetDatum(acoerce->resulttypmod);
- locfcinfo.arg[2] = BoolGetDatum(acoerce->isExplicit);
- locfcinfo.argnull[0] = false;
- locfcinfo.argnull[1] = false;
- locfcinfo.argnull[2] = false;
-
- return array_map(&locfcinfo, astate->resultelemtype, astate->amstate);
-}
-
-/* ----------------------------------------------------------------
- * ExecEvalCurrentOfExpr
- *
- * The planner should convert CURRENT OF into a TidScan qualification, or some
- * other special handling in a ForeignScan node. So we have to be able to do
- * ExecInitExpr on a CurrentOfExpr, but we shouldn't ever actually execute it.
- * If we get here, we suppose we must be dealing with CURRENT OF on a foreign
- * table whose FDW doesn't handle it, and complain accordingly.
- * ----------------------------------------------------------------
- */
-static Datum
-ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
-{
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("WHERE CURRENT OF is not supported for this table type")));
- return 0; /* keep compiler quiet */
-}
-
-
-/*
- * ExecEvalExprSwitchContext
- *
- * Same as ExecEvalExpr, but get into the right allocation context explicitly.
- */
-Datum
-ExecEvalExprSwitchContext(ExprState *expression,
- ExprContext *econtext,
- bool *isNull)
-{
- Datum retDatum;
- MemoryContext oldContext;
-
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- retDatum = ExecEvalExpr(expression, econtext, isNull);
- 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, WindowFunc, or SubPlan nodes found in the tree are added to the
- * lists of such nodes held by the parent PlanState. Otherwise, we do very
- * little initialization here other than building the state-node tree. Any
- * nontrivial work associated with initializing runtime info for a node should
- * happen during the first actual evaluation of that node. (This policy lets
- * us avoid work if the node is never actually evaluated.)
- *
- * Note: there is no ExecEndExpr function; we assume that any resource
- * cleanup needed will be handled by just releasing the memory context
- * in which the state tree is built. Functions that require additional
- * cleanup work can register a shutdown callback in the ExprContext.
- *
- * 'node' is the root of the expression tree to examine
- * 'parent' is the PlanState node that owns the expression.
- *
- * 'parent' may be NULL if we are preparing an expression that is not
- * associated with a plan tree. (If so, it can't have aggs or subplans.)
- * This case should usually come through ExecPrepareExpr, not directly here.
- */
-ExprState *
-ExecInitExpr(Expr *node, PlanState *parent)
-{
- ExprState *state;
-
- if (node == NULL)
- return NULL;
-
- /* Guard against stack overflow due to overly complex expressions */
- check_stack_depth();
-
- switch (nodeTag(node))
- {
- case T_Var:
- /* varattno == InvalidAttrNumber means it's a whole-row Var */
- if (((Var *) node)->varattno == InvalidAttrNumber)
- {
- WholeRowVarExprState *wstate = makeNode(WholeRowVarExprState);
-
- wstate->parent = parent;
- wstate->wrv_tupdesc = NULL;
- wstate->wrv_junkFilter = NULL;
- state = (ExprState *) wstate;
- state->evalfunc = (ExprStateEvalFunc) ExecEvalWholeRowVar;
- }
- else
- {
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalScalarVar;
- }
- break;
- case T_Const:
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalConst;
- break;
- case T_Param:
- state = makeNode(ExprState);
- switch (((Param *) node)->paramkind)
- {
- case PARAM_EXEC:
- state->evalfunc = ExecEvalParamExec;
- break;
- case PARAM_EXTERN:
- state->evalfunc = ExecEvalParamExtern;
- break;
- default:
- elog(ERROR, "unrecognized paramkind: %d",
- (int) ((Param *) node)->paramkind);
- break;
- }
- break;
- case T_CoerceToDomainValue:
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalCoerceToDomainValue;
- break;
- case T_CaseTestExpr:
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalCaseTestExpr;
- break;
- case T_Aggref:
- {
- AggrefExprState *astate = makeNode(AggrefExprState);
-
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
- if (parent && IsA(parent, AggState))
- {
- AggState *aggstate = (AggState *) parent;
-
- aggstate->aggs = lcons(astate, aggstate->aggs);
- aggstate->numaggs++;
- }
- else
- {
- /* planner messed up */
- elog(ERROR, "Aggref found in non-Agg plan node");
- }
- state = (ExprState *) astate;
- }
- break;
- case T_GroupingFunc:
- {
- GroupingFunc *grp_node = (GroupingFunc *) node;
- GroupingFuncExprState *grp_state = makeNode(GroupingFuncExprState);
- Agg *agg = NULL;
-
- if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
- elog(ERROR, "parent of GROUPING is not Agg node");
-
- grp_state->aggstate = (AggState *) parent;
-
- agg = (Agg *) (parent->plan);
-
- if (agg->groupingSets)
- grp_state->clauses = grp_node->cols;
- else
- grp_state->clauses = NIL;
-
- state = (ExprState *) grp_state;
- state->evalfunc = (ExprStateEvalFunc) ExecEvalGroupingFuncExpr;
- }
- break;
- case T_WindowFunc:
- {
- WindowFunc *wfunc = (WindowFunc *) node;
- WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
-
- wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
- if (parent && IsA(parent, WindowAggState))
- {
- WindowAggState *winstate = (WindowAggState *) parent;
- int nfuncs;
-
- winstate->funcs = lcons(wfstate, winstate->funcs);
- nfuncs = ++winstate->numfuncs;
- if (wfunc->winagg)
- winstate->numaggs++;
-
- wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
- parent);
- wfstate->aggfilter = ExecInitExpr(wfunc->aggfilter,
- parent);
-
- /*
- * Complain if the windowfunc's arguments contain any
- * windowfuncs; nested window functions are semantically
- * nonsensical. (This should have been caught earlier,
- * but we defend against it here anyway.)
- */
- if (nfuncs != winstate->numfuncs)
- ereport(ERROR,
- (errcode(ERRCODE_WINDOWING_ERROR),
- errmsg("window function calls cannot be nested")));
- }
- else
- {
- /* planner messed up */
- elog(ERROR, "WindowFunc found in non-WindowAgg plan node");
- }
- state = (ExprState *) wfstate;
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
-
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
- astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
- astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
- parent);
- /* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
- state = (ExprState *) astate;
- }
- break;
- case T_FuncExpr:
- {
- FuncExpr *funcexpr = (FuncExpr *) node;
- FuncExprState *fstate = makeNode(FuncExprState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
- fstate->args = (List *)
- ExecInitExpr((Expr *) funcexpr->args, parent);
- fstate->func.fn_oid = InvalidOid; /* not initialized */
- fstate->funcReturnsSet = funcexpr->funcretset;
- state = (ExprState *) fstate;
- }
- break;
- case T_OpExpr:
- {
- OpExpr *opexpr = (OpExpr *) node;
- FuncExprState *fstate = makeNode(FuncExprState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
- fstate->args = (List *)
- ExecInitExpr((Expr *) opexpr->args, parent);
- fstate->func.fn_oid = InvalidOid; /* not initialized */
- fstate->funcReturnsSet = opexpr->opretset;
- state = (ExprState *) fstate;
- }
- break;
- case T_DistinctExpr:
- {
- DistinctExpr *distinctexpr = (DistinctExpr *) node;
- FuncExprState *fstate = makeNode(FuncExprState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
- fstate->args = (List *)
- ExecInitExpr((Expr *) distinctexpr->args, parent);
- fstate->func.fn_oid = InvalidOid; /* not initialized */
- fstate->funcReturnsSet = false; /* not supported */
- state = (ExprState *) fstate;
- }
- break;
- case T_NullIfExpr:
- {
- NullIfExpr *nullifexpr = (NullIfExpr *) node;
- FuncExprState *fstate = makeNode(FuncExprState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
- fstate->args = (List *)
- ExecInitExpr((Expr *) nullifexpr->args, parent);
- fstate->func.fn_oid = InvalidOid; /* not initialized */
- fstate->funcReturnsSet = false; /* not supported */
- state = (ExprState *) fstate;
- }
- break;
- case T_ScalarArrayOpExpr:
- {
- ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
- ScalarArrayOpExprState *sstate = makeNode(ScalarArrayOpExprState);
-
- sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
- sstate->fxprstate.args = (List *)
- ExecInitExpr((Expr *) opexpr->args, parent);
- sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
- sstate->fxprstate.funcReturnsSet = false; /* not supported */
- sstate->element_type = InvalidOid; /* ditto */
- state = (ExprState *) sstate;
- }
- break;
- case T_BoolExpr:
- {
- BoolExpr *boolexpr = (BoolExpr *) node;
- BoolExprState *bstate = makeNode(BoolExprState);
-
- switch (boolexpr->boolop)
- {
- case AND_EXPR:
- bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAnd;
- break;
- case OR_EXPR:
- bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOr;
- break;
- case NOT_EXPR:
- bstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNot;
- break;
- default:
- elog(ERROR, "unrecognized boolop: %d",
- (int) boolexpr->boolop);
- break;
- }
- bstate->args = (List *)
- ExecInitExpr((Expr *) boolexpr->args, parent);
- state = (ExprState *) bstate;
- }
- break;
- case T_SubPlan:
- {
- SubPlan *subplan = (SubPlan *) node;
- SubPlanState *sstate;
-
- if (!parent)
- elog(ERROR, "SubPlan found with no parent plan");
-
- sstate = ExecInitSubPlan(subplan, parent);
-
- /* Add SubPlanState nodes to parent->subPlan */
- parent->subPlan = lappend(parent->subPlan, sstate);
-
- state = (ExprState *) sstate;
- }
- break;
- case T_AlternativeSubPlan:
- {
- AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
- AlternativeSubPlanState *asstate;
-
- if (!parent)
- elog(ERROR, "AlternativeSubPlan found with no parent plan");
-
- asstate = ExecInitAlternativeSubPlan(asplan, parent);
-
- state = (ExprState *) asstate;
- }
- break;
- case T_FieldSelect:
- {
- FieldSelect *fselect = (FieldSelect *) node;
- FieldSelectState *fstate = makeNode(FieldSelectState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
- fstate->arg = ExecInitExpr(fselect->arg, parent);
- fstate->argdesc = NULL;
- state = (ExprState *) fstate;
- }
- break;
- case T_FieldStore:
- {
- FieldStore *fstore = (FieldStore *) node;
- FieldStoreState *fstate = makeNode(FieldStoreState);
-
- fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
- fstate->arg = ExecInitExpr(fstore->arg, parent);
- fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
- fstate->argdesc = NULL;
- state = (ExprState *) fstate;
- }
- break;
- case T_RelabelType:
- {
- RelabelType *relabel = (RelabelType *) node;
- GenericExprState *gstate = makeNode(GenericExprState);
-
- gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
- gstate->arg = ExecInitExpr(relabel->arg, parent);
- state = (ExprState *) gstate;
- }
- break;
- case T_CoerceViaIO:
- {
- CoerceViaIO *iocoerce = (CoerceViaIO *) node;
- CoerceViaIOState *iostate = makeNode(CoerceViaIOState);
- Oid iofunc;
- bool typisvarlena;
-
- iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
- iostate->arg = ExecInitExpr(iocoerce->arg, parent);
- /* lookup the result type's input function */
- getTypeInputInfo(iocoerce->resulttype, &iofunc,
- &iostate->intypioparam);
- fmgr_info(iofunc, &iostate->infunc);
- /* lookup the input type's output function */
- getTypeOutputInfo(exprType((Node *) iocoerce->arg),
- &iofunc, &typisvarlena);
- fmgr_info(iofunc, &iostate->outfunc);
- state = (ExprState *) iostate;
- }
- break;
- case T_ArrayCoerceExpr:
- {
- ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
- ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
-
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
- astate->arg = ExecInitExpr(acoerce->arg, parent);
- astate->resultelemtype = get_element_type(acoerce->resulttype);
- if (astate->resultelemtype == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("target type is not an array")));
- /* Arrays over domains aren't supported yet */
- Assert(getBaseType(astate->resultelemtype) ==
- astate->resultelemtype);
- astate->elemfunc.fn_oid = InvalidOid; /* not initialized */
- astate->amstate = (ArrayMapState *) palloc0(sizeof(ArrayMapState));
- state = (ExprState *) astate;
- }
- break;
- case T_ConvertRowtypeExpr:
- {
- ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
- ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
-
- cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
- cstate->arg = ExecInitExpr(convert->arg, parent);
- state = (ExprState *) cstate;
- }
- break;
- case T_CaseExpr:
- {
- CaseExpr *caseexpr = (CaseExpr *) node;
- CaseExprState *cstate = makeNode(CaseExprState);
- List *outlist = NIL;
- ListCell *l;
-
- cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
- cstate->arg = ExecInitExpr(caseexpr->arg, parent);
- foreach(l, caseexpr->args)
- {
- CaseWhen *when = castNode(CaseWhen, lfirst(l));
- CaseWhenState *wstate = makeNode(CaseWhenState);
-
- wstate->xprstate.evalfunc = NULL; /* not used */
- wstate->xprstate.expr = (Expr *) when;
- wstate->expr = ExecInitExpr(when->expr, parent);
- wstate->result = ExecInitExpr(when->result, parent);
- outlist = lappend(outlist, wstate);
- }
- cstate->args = outlist;
- cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
- if (caseexpr->arg)
- cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
- state = (ExprState *) cstate;
- }
- break;
- case T_ArrayExpr:
- {
- ArrayExpr *arrayexpr = (ArrayExpr *) node;
- ArrayExprState *astate = makeNode(ArrayExprState);
- List *outlist = NIL;
- ListCell *l;
-
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArray;
- foreach(l, arrayexpr->elements)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- astate->elements = outlist;
- /* do one-time catalog lookup for type info */
- get_typlenbyvalalign(arrayexpr->element_typeid,
- &astate->elemlength,
- &astate->elembyval,
- &astate->elemalign);
- state = (ExprState *) astate;
- }
- break;
- case T_RowExpr:
- {
- RowExpr *rowexpr = (RowExpr *) node;
- RowExprState *rstate = makeNode(RowExprState);
- Form_pg_attribute *attrs;
- List *outlist = NIL;
- ListCell *l;
- int i;
-
- rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
- /* Build tupdesc to describe result tuples */
- if (rowexpr->row_typeid == RECORDOID)
- {
- /* generic record, use types of given expressions */
- rstate->tupdesc = ExecTypeFromExprList(rowexpr->args);
- }
- else
- {
- /* it's been cast to a named type, use that */
- rstate->tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
- }
- /* In either case, adopt RowExpr's column aliases */
- ExecTypeSetColNames(rstate->tupdesc, rowexpr->colnames);
- /* Bless the tupdesc in case it's now of type RECORD */
- BlessTupleDesc(rstate->tupdesc);
- /* Set up evaluation, skipping any deleted columns */
- Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
- attrs = rstate->tupdesc->attrs;
- i = 0;
- foreach(l, rowexpr->args)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- if (!attrs[i]->attisdropped)
- {
- /*
- * Guard against ALTER COLUMN TYPE on rowtype since
- * the RowExpr was created. XXX should we check
- * typmod too? Not sure we can be sure it'll be the
- * same.
- */
- if (exprType((Node *) e) != attrs[i]->atttypid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("ROW() column has type %s instead of type %s",
- format_type_be(exprType((Node *) e)),
- format_type_be(attrs[i]->atttypid))));
- }
- else
- {
- /*
- * Ignore original expression and insert a NULL. We
- * don't really care what type of NULL it is, so
- * always make an int4 NULL.
- */
- e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
- }
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- i++;
- }
- rstate->args = outlist;
- state = (ExprState *) rstate;
- }
- break;
- case T_RowCompareExpr:
- {
- RowCompareExpr *rcexpr = (RowCompareExpr *) node;
- RowCompareExprState *rstate = makeNode(RowCompareExprState);
- int nopers = list_length(rcexpr->opnos);
- List *outlist;
- ListCell *l;
- ListCell *l2;
- ListCell *l3;
- int i;
-
- rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRowCompare;
- Assert(list_length(rcexpr->largs) == nopers);
- outlist = NIL;
- foreach(l, rcexpr->largs)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- rstate->largs = outlist;
- Assert(list_length(rcexpr->rargs) == nopers);
- outlist = NIL;
- foreach(l, rcexpr->rargs)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- rstate->rargs = outlist;
- Assert(list_length(rcexpr->opfamilies) == nopers);
- rstate->funcs = (FmgrInfo *) palloc(nopers * sizeof(FmgrInfo));
- rstate->collations = (Oid *) palloc(nopers * sizeof(Oid));
- i = 0;
- forthree(l, rcexpr->opnos, l2, rcexpr->opfamilies, l3, rcexpr->inputcollids)
- {
- Oid opno = lfirst_oid(l);
- Oid opfamily = lfirst_oid(l2);
- Oid inputcollid = lfirst_oid(l3);
- int strategy;
- Oid lefttype;
- Oid righttype;
- Oid proc;
-
- get_op_opfamily_properties(opno, opfamily, false,
- &strategy,
- &lefttype,
- &righttype);
- proc = get_opfamily_proc(opfamily,
- lefttype,
- righttype,
- BTORDER_PROC);
-
- /*
- * If we enforced permissions checks on index support
- * functions, we'd need to make a check here. But the
- * index support machinery doesn't do that, and neither
- * does this code.
- */
- fmgr_info(proc, &(rstate->funcs[i]));
- rstate->collations[i] = inputcollid;
- i++;
- }
- state = (ExprState *) rstate;
- }
- break;
- case T_CoalesceExpr:
- {
- CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
- CoalesceExprState *cstate = makeNode(CoalesceExprState);
- List *outlist = NIL;
- ListCell *l;
-
- cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoalesce;
- foreach(l, coalesceexpr->args)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- cstate->args = outlist;
- state = (ExprState *) cstate;
- }
- break;
- case T_MinMaxExpr:
- {
- MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
- MinMaxExprState *mstate = makeNode(MinMaxExprState);
- List *outlist = NIL;
- ListCell *l;
- TypeCacheEntry *typentry;
-
- mstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalMinMax;
- foreach(l, minmaxexpr->args)
- {
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- mstate->args = outlist;
- /* Look up the btree comparison function for the datatype */
- typentry = lookup_type_cache(minmaxexpr->minmaxtype,
- TYPECACHE_CMP_PROC);
- if (!OidIsValid(typentry->cmp_proc))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_FUNCTION),
- errmsg("could not identify a comparison function for type %s",
- format_type_be(minmaxexpr->minmaxtype))));
-
- /*
- * If we enforced permissions checks on index support
- * functions, we'd need to make a check here. But the index
- * support machinery doesn't do that, and neither does this
- * code.
- */
- fmgr_info(typentry->cmp_proc, &(mstate->cfunc));
- state = (ExprState *) mstate;
- }
- break;
- case T_SQLValueFunction:
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalSQLValueFunction;
- break;
- case T_XmlExpr:
- {
- XmlExpr *xexpr = (XmlExpr *) node;
- XmlExprState *xstate = makeNode(XmlExprState);
- List *outlist;
- ListCell *arg;
-
- xstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalXml;
- outlist = NIL;
- foreach(arg, xexpr->named_args)
- {
- Expr *e = (Expr *) lfirst(arg);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- xstate->named_args = outlist;
-
- outlist = NIL;
- foreach(arg, xexpr->args)
- {
- Expr *e = (Expr *) lfirst(arg);
- ExprState *estate;
-
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
- }
- xstate->args = outlist;
-
- state = (ExprState *) xstate;
- }
- break;
- case T_NullTest:
- {
- NullTest *ntest = (NullTest *) node;
- NullTestState *nstate = makeNode(NullTestState);
-
- nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
- nstate->arg = ExecInitExpr(ntest->arg, parent);
- nstate->argdesc = NULL;
- state = (ExprState *) nstate;
- }
- break;
- case T_BooleanTest:
- {
- BooleanTest *btest = (BooleanTest *) node;
- GenericExprState *gstate = makeNode(GenericExprState);
-
- gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
- gstate->arg = ExecInitExpr(btest->arg, parent);
- state = (ExprState *) gstate;
- }
- break;
- case T_CoerceToDomain:
- {
- CoerceToDomain *ctest = (CoerceToDomain *) node;
- CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
-
- cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
- cstate->arg = ExecInitExpr(ctest->arg, parent);
- /* We spend an extra palloc to reduce header inclusions */
- cstate->constraint_ref = (DomainConstraintRef *)
- palloc(sizeof(DomainConstraintRef));
- InitDomainConstraintRef(ctest->resulttype,
- cstate->constraint_ref,
- CurrentMemoryContext);
- state = (ExprState *) cstate;
- }
- break;
- case T_CurrentOfExpr:
- state = makeNode(ExprState);
- state->evalfunc = ExecEvalCurrentOfExpr;
- break;
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
- GenericExprState *gstate = makeNode(GenericExprState);
-
- gstate->xprstate.evalfunc = NULL; /* not used */
- gstate->arg = ExecInitExpr(tle->expr, parent);
- state = (ExprState *) gstate;
- }
- break;
- case T_List:
- {
- List *outlist = NIL;
- ListCell *l;
-
- foreach(l, (List *) node)
- {
- outlist = lappend(outlist,
- ExecInitExpr((Expr *) lfirst(l),
- parent));
- }
- /* Don't fall through to the "common" code below */
- return (ExprState *) outlist;
- }
- default:
- elog(ERROR, "unrecognized node type: %d",
- (int) nodeTag(node));
- state = NULL; /* keep compiler quiet */
- break;
- }
-
- /* Common code for all state-node types */
- state->expr = node;
-
- return state;
-}
-
-/*
- * 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 run the
- * passed expression tree through expression_planner() to prepare it for
- * execution. (In ordinary Plan trees the regular planning process will have
- * made the appropriate transformations on expressions, but for standalone
- * expressions this won't have happened.)
- */
-ExprState *
-ExecPrepareExpr(Expr *node, EState *estate)
-{
- ExprState *result;
- MemoryContext oldcontext;
-
- oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
-
- node = expression_planner(node);
-
- result = ExecInitExpr(node, NULL);
-
- MemoryContextSwitchTo(oldcontext);
-
- return result;
-}
-
-
-/* ----------------------------------------------------------------
- * ExecQual / ExecTargetList / ExecProject
- * ----------------------------------------------------------------
- */
-
-/* ----------------------------------------------------------------
- * ExecQual
- *
- * Evaluates a conjunctive boolean expression (qual list) and
- * returns true iff none of the subexpressions are false.
- * (We also return true if the list is empty.)
- *
- * If some of the subexpressions yield NULL but none yield FALSE,
- * then the result of the conjunction is NULL (ie, unknown)
- * according to three-valued boolean logic. In this case,
- * we return the value specified by the "resultForNull" parameter.
- *
- * Callers evaluating WHERE clauses should pass resultForNull=FALSE,
- * since SQL specifies that tuples with null WHERE results do not
- * get selected. On the other hand, callers evaluating constraint
- * conditions should pass resultForNull=TRUE, since SQL also specifies
- * that NULL constraint conditions are not failures.
- *
- * NOTE: it would not be correct to use this routine to evaluate an
- * AND subclause of a boolean expression; for that purpose, a NULL
- * result must be returned as NULL so that it can be properly treated
- * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr).
- * This routine is only used in contexts where a complete expression
- * is being evaluated and we know that NULL can be treated the same
- * as one boolean result or the other.
- *
- * ----------------------------------------------------------------
- */
-bool
-ExecQual(List *qual, ExprContext *econtext, bool resultForNull)
-{
- bool result;
- MemoryContext oldContext;
- ListCell *l;
-
- /*
- * debugging stuff
- */
- EV_printf("ExecQual: qual is ");
- EV_nodeDisplay(qual);
- EV_printf("\n");
-
- /*
- * Run in short-lived per-tuple context while computing expressions.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * Evaluate the qual conditions one at a time. If we find a FALSE result,
- * we can stop evaluating and return FALSE --- the AND result must be
- * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we
- * can stop and return FALSE --- the AND result must be FALSE or NULL in
- * that case, and the caller doesn't care which.
- *
- * If we get to the end of the list, we can return TRUE. This will happen
- * when the AND result is indeed TRUE, or when the AND result is NULL (one
- * or more NULL subresult, with all the rest TRUE) and the caller has
- * specified resultForNull = TRUE.
- */
- result = true;
-
- foreach(l, qual)
- {
- ExprState *clause = (ExprState *) lfirst(l);
- Datum expr_value;
- bool isNull;
-
- expr_value = ExecEvalExpr(clause, econtext, &isNull);
-
- if (isNull)
- {
- if (resultForNull == false)
- {
- result = false; /* treat NULL as FALSE */
- break;
- }
- }
- else
- {
- if (!DatumGetBool(expr_value))
- {
- result = false; /* definitely FALSE */
- break;
- }
- }
- }
-
- MemoryContextSwitchTo(oldContext);
-
- return result;
-}
-
-/*
- * Number of items in a tlist (including any resjunk items!)
- */
-int
-ExecTargetListLength(List *targetlist)
-{
- /* This used to be more complex, but fjoins are dead */
- return list_length(targetlist);
-}
-
-/*
- * Number of items in a tlist, not including any resjunk items
- */
-int
-ExecCleanTargetListLength(List *targetlist)
-{
- int len = 0;
- ListCell *tl;
-
- foreach(tl, targetlist)
- {
- TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
-
- if (!curTle->resjunk)
- len++;
- }
- return len;
-}
-
-/*
- * ExecTargetList
- * Evaluates a targetlist with respect to the given
- * expression context.
- *
- * tupdesc must describe the rowtype of the expected result.
- *
- * Results are stored into the passed values and isnull arrays.
- *
- * Since fields of the result tuple might be multiply referenced in higher
- * plan nodes, we have to force any read/write expanded values to read-only
- * status. It's a bit annoying to have to do that for every projected
- * expression; in the future, consider teaching the planner to detect
- * actually-multiply-referenced Vars and insert an expression node that
- * would do that only where really required.
- */
-static void
-ExecTargetList(List *targetlist,
- TupleDesc tupdesc,
- ExprContext *econtext,
- Datum *values,
- bool *isnull)
-{
- Form_pg_attribute *att = tupdesc->attrs;
- MemoryContext oldContext;
- ListCell *tl;
-
- /*
- * Run in short-lived per-tuple context while computing expressions.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
-
- /*
- * evaluate all the expressions in the target list
- */
- foreach(tl, targetlist)
- {
- GenericExprState *gstate = (GenericExprState *) lfirst(tl);
- TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
- AttrNumber resind = tle->resno - 1;
-
- values[resind] = ExecEvalExpr(gstate->arg,
- econtext,
- &isnull[resind]);
-
- values[resind] = MakeExpandedObjectReadOnly(values[resind],
- isnull[resind],
- att[resind]->attlen);
- }
-
- MemoryContextSwitchTo(oldContext);
-}
-
-/*
- * ExecProject
- *
- * projects a tuple based on projection info and stores
- * it in the previously specified tuple table slot.
- *
- * Note: the result is always a virtual tuple; therefore it
- * may reference the contents of the exprContext's scan tuples
- * and/or temporary results constructed in the exprContext.
- * If the caller wishes the result to be valid longer than that
- * data will be valid, he must call ExecMaterializeSlot on the
- * result slot.
- */
-TupleTableSlot *
-ExecProject(ProjectionInfo *projInfo)
-{
- TupleTableSlot *slot;
- ExprContext *econtext;
- int numSimpleVars;
-
- /*
- * sanity checks
- */
- Assert(projInfo != NULL);
-
- /*
- * get the projection info we want
- */
- slot = projInfo->pi_slot;
- econtext = projInfo->pi_exprContext;
-
- /*
- * Clear any former contents of the result slot. This makes it safe for
- * us to use the slot's Datum/isnull arrays as workspace.
- */
- ExecClearTuple(slot);
-
- /*
- * Force extraction of all input values that we'll need. The
- * Var-extraction loops below depend on this, and we are also prefetching
- * all attributes that will be referenced in the generic expressions.
- */
- if (projInfo->pi_lastInnerVar > 0)
- slot_getsomeattrs(econtext->ecxt_innertuple,
- projInfo->pi_lastInnerVar);
- if (projInfo->pi_lastOuterVar > 0)
- slot_getsomeattrs(econtext->ecxt_outertuple,
- projInfo->pi_lastOuterVar);
- if (projInfo->pi_lastScanVar > 0)
- slot_getsomeattrs(econtext->ecxt_scantuple,
- projInfo->pi_lastScanVar);
-
- /*
- * Assign simple Vars to result by direct extraction of fields from source
- * slots ... a mite ugly, but fast ...
- */
- numSimpleVars = projInfo->pi_numSimpleVars;
- if (numSimpleVars > 0)
- {
- Datum *values = slot->tts_values;
- bool *isnull = slot->tts_isnull;
- int *varSlotOffsets = projInfo->pi_varSlotOffsets;
- int *varNumbers = projInfo->pi_varNumbers;
- int i;
-
- if (projInfo->pi_directMap)
- {
- /* especially simple case where vars go to output in order */
- for (i = 0; i < numSimpleVars; i++)
- {
- char *slotptr = ((char *) econtext) + varSlotOffsets[i];
- TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
- int varNumber = varNumbers[i] - 1;
-
- values[i] = varSlot->tts_values[varNumber];
- isnull[i] = varSlot->tts_isnull[varNumber];
- }
- }
- else
- {
- /* we have to pay attention to varOutputCols[] */
- int *varOutputCols = projInfo->pi_varOutputCols;
-
- for (i = 0; i < numSimpleVars; i++)
- {
- char *slotptr = ((char *) econtext) + varSlotOffsets[i];
- TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr);
- int varNumber = varNumbers[i] - 1;
- int varOutputCol = varOutputCols[i] - 1;
-
- values[varOutputCol] = varSlot->tts_values[varNumber];
- isnull[varOutputCol] = varSlot->tts_isnull[varNumber];
- }
- }
- }
-
- /*
- * If there are any generic expressions, evaluate them.
- */
- if (projInfo->pi_targetlist)
- {
- ExecTargetList(projInfo->pi_targetlist,
- slot->tts_tupleDescriptor,
- econtext,
- slot->tts_values,
- slot->tts_isnull);
- }
-
- /*
- * Mark the result slot as containing a valid virtual tuple.
- */
- return ExecStoreVirtualTuple(slot);
-}