* benefit, and it'd require postgres_fdw to know more than is desirable
* about Param evaluation.)
*/
- *param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node);
+ *param_exprs = ExecInitExprList(fdw_exprs, node);
/* Allocate buffer for text form of query parameters. */
*param_values = (const char **) palloc0(numParams * sizeof(char *));
/* predicate will likely be null, but may as well copy it */
newind->il_info->ii_Predicate = (List *)
copyObject(indexInfo->ii_Predicate);
- newind->il_info->ii_PredicateState = NIL;
+ newind->il_info->ii_PredicateState = NULL;
/* no exclusion constraints at bootstrap time, so no need to copy */
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(indexInfo->ii_ExclusionProcs == NULL);
/* fetch index predicate if any */
ii->ii_Predicate = RelationGetIndexPredicate(index);
- ii->ii_PredicateState = NIL;
+ ii->ii_PredicateState = NULL;
/* fetch exclusion constraint info if any */
if (indexStruct->indisexclusion)
indexInfo->ii_ExpressionsState == NIL)
{
/* First time through, set up expression evaluation state */
- indexInfo->ii_ExpressionsState = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
- estate);
+ indexInfo->ii_ExpressionsState =
+ ExecPrepareExprList(indexInfo->ii_Expressions, estate);
/* Check caller has set up context correctly */
Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
}
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
double reltuples;
- List *predicate;
+ ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Prepare for scan of the base relation. In a normal index build, we use
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
- if (predicate != NIL)
+ if (predicate != NULL)
{
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
return reltuples;
}
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
- List *predicate;
+ ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Scan all live tuples in the base relation.
/*
* In a partial index, ignore tuples that don't satisfy the predicate.
*/
- if (predicate != NIL)
+ if (predicate != NULL)
{
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
}
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
- List *predicate;
+ ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Prepare for scan of the base relation. We need just those tuples
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
- if (predicate != NIL)
+ if (predicate != NULL)
{
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
}
GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
/* First time through, set up expression evaluation state */
- pd->keystate = (List *) ExecPrepareExpr((Expr *) pd->key->partexprs,
- estate);
+ pd->keystate = ExecPrepareExprList(pd->key->partexprs,
+ estate);
}
partexpr_item = list_head(pd->keystate);
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = NIL;
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
- List *predicate;
+ ExprState *predicate;
Datum *exprvals;
bool *exprnulls;
int numindexrows,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
- predicate = castNode(List,
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate));
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/* Compute and save index expression values */
exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/* If index is partial, check predicate */
- if (predicate != NIL)
+ if (predicate != NULL)
{
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
numindexrows++;
Assert(CurrentMemoryContext == econtext->ecxt_per_tuple_memory);
values[defmap[i]] = ExecEvalExpr(defexprs[i], econtext,
- &nulls[defmap[i]]);
+ &nulls[defmap[i]]);
}
return true;
foreach(lst, plans)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
- SubPlan *sp = (SubPlan *) sps->xprstate.expr;
+ SubPlan *sp = sps->subplan;
/*
* There can be multiple SubPlan nodes referencing the same physical
indexInfo = makeNode(IndexInfo);
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
indexInfo->ii_Expressions = NIL; /* for now */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
- indexInfo->ii_PredicateState = NIL;
+ indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
}
/* Prepare the expressions for execution */
- exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
+ exprstates = ExecPrepareExprList(params, estate);
paramLI = (ParamListInfo)
palloc(offsetof(ParamListInfoData, params) +
i = 0;
foreach(l, exprstates)
{
- ExprState *n = lfirst(l);
+ ExprState *n = (ExprState *) lfirst(l);
ParamExternData *prm = ¶mLI->params[i];
prm->ptype = param_types[i];
Oid refindid; /* OID of PK's index, if FOREIGN */
Oid conid; /* OID of pg_constraint entry, if FOREIGN */
Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
- List *qualstate; /* Execution state for CHECK */
+ ExprState *qualstate; /* Execution state for CHECK */
} NewConstraint;
/*
CommandId mycid;
BulkInsertState bistate;
int hi_options;
- List *partqualstate = NIL;
+ ExprState *partqualstate = NULL;
/*
* Open the relation(s). We have surely already locked the existing
{
case CONSTR_CHECK:
needscan = true;
- con->qualstate = (List *)
- ExecPrepareExpr((Expr *) con->qual, estate);
+ con->qualstate = ExecPrepareQual((List *) con->qual, estate);
break;
case CONSTR_FOREIGN:
/* Nothing to do here */
if (tab->partition_constraint)
{
needscan = true;
- partqualstate = (List *)
- ExecPrepareExpr((Expr *) tab->partition_constraint,
- estate);
+ partqualstate = ExecPrepareCheck(tab->partition_constraint, estate);
}
foreach(l, tab->newvals)
switch (con->contype)
{
case CONSTR_CHECK:
- if (!ExecQual(con->qualstate, econtext, true))
+ if (!ExecQual(con->qualstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
}
}
- if (partqualstate && !ExecQual(partqualstate, econtext, true))
+ if (partqualstate && !ExecCheck(partqualstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint is violated by some row")));
Datum val;
char *conbin;
Expr *origexpr;
- List *exprstate;
+ ExprState *exprstate;
TupleDesc tupdesc;
HeapScanDesc scan;
HeapTuple tuple;
HeapTupleGetOid(constrtup));
conbin = TextDatumGetCString(val);
origexpr = (Expr *) stringToNode(conbin);
- exprstate = (List *)
- ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
+ exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate);
econtext = GetPerTupleExprContext(estate);
tupdesc = RelationGetDescr(rel);
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
- if (!ExecQual(exprstate, econtext, true))
+ if (!ExecQual(exprstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
if (trigger->tgqual)
{
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
- List **predicate;
+ ExprState **predicate;
ExprContext *econtext;
TupleTableSlot *oldslot = NULL;
TupleTableSlot *newslot = NULL;
* nodetrees for it. Keep them in the per-query memory context so
* they'll survive throughout the query.
*/
- if (*predicate == NIL)
+ if (*predicate == NULL)
{
Node *tgqual;
ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
/* ExecQual wants implicit-AND form */
tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
- *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
+ *predicate = ExecPrepareQual((List *) tgqual, estate);
MemoryContextSwitchTo(oldContext);
}
*/
econtext->ecxt_innertuple = oldslot;
econtext->ecxt_outertuple = newslot;
- if (!ExecQual(*predicate, econtext, false))
+ if (!ExecQual(*predicate, econtext))
return false;
}
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
-OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
- execMain.o execParallel.o execProcnode.o execQual.o \
- execReplication.o execScan.o execTuples.o \
+OBJS = execAmi.o execCurrent.o execExpr.o execInterpExpr.o execGrouping.o \
+ execIndexing.o execJunk.o execMain.o execParallel.o execProcnode.o \
+ execQual.o execReplication.o execScan.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ * Expression evaluation infrastructure.
+ *
+ * Expression evaluation now works by first converting expression trees
+ * (which went through planning first) into an ExprState using ExecInitExpr()
+ * et al. This converts the tree into a opcode based program (ExprEvalStep
+ * representing individual instructions); allocated as a flat array of
+ * steps.
+ *
+ * This flat representation has the big advantage that it can be implemented
+ * non-recursively, within a single function. This allows to interpret
+ * larger expressions from largely within a single function, keeping state
+ * between them. In contrast to that, tree-walk based approaches in contrast
+ * often run into performance issues due to function call / stack
+ * manipulation overhead.
+ *
+ * The ExprEvalStep representation is designed to be usable for interpreting
+ * the expression, as well as compiling into native code. Thus, if possible,
+ * as much complexity as possible should be handed by ExecInitExpr() (and
+ * helpers), instead of handled at execution time where both interpreted and
+ * compiled versions would need to deal with the complexity. Additionally
+ * checks for initialization at run time have a small but noticeable cost at
+ * every execution.
+ *
+ * The next step is preparing the ExprState for execution, using
+ * ExecInstantiateExpr(). This is internally done by ExecInitExpr() and other
+ * functions that prepare for expression evaluation. ExecInstantiateExpr()
+ * initializes the expression for the relevant method chosen to evaluate the
+ * expression.
+ *
+ * Note that a lot of the more complex expression evaluation steps, which are
+ * less performance critical than some of the simpler and more common ones,
+ * are implemented as separate functions outside the fast-path of interpreted
+ * expression, like e.g. ExecEvalRow(), so that the implementation can be
+ * shared between interpreted and compiled expression evaluation. That means
+ * that ExecInstantiateExpr() always has to initialize the expression for
+ * evaluation by execInterpExpr.c. It also means that these helper functions
+ * are not "allowed" to perform dispatch themselves, as the method of
+ * dispatch will vary based on the caller.
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ *
+ * IDENTIFICATION
+ * src/backend/executor/execExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "access/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.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"
+
+
+/*
+ * Support for building execution state.
+ */
+static void ExecInstantiateExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
+ Oid funcid, Oid inputcollid, PlanState *parent,
+ ExprState *state, Datum *resv, bool *resnull);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+
+/* support functions */
+static bool isAssignmentIndirectionExpr(Expr *expr);
+
+
+/*
+ * ExecInitExpr: prepare an expression tree for execution
+ *
+ * This function builds and returns an ExprState implementing the given
+ * Expr node tree. The return ExprState 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.
+ *
+ * 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 = makeNode(ExprState);
+ ExprEvalStep scratch;
+
+ if (node == NULL)
+ return NULL;
+
+ state->expr = node;
+ ExecInitExprSlots(state, (Node *) node);
+
+ ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+ scratch.resvalue = &state->resvalue;
+ scratch.resnull = &state->resnull;
+ scratch.opcode = EEO_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecInstantiateExpr(state);
+
+ return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that 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, then the result of the
+ * conjunction is false. This makes this routine primarily useful for
+ * evaluating WHERE clauses, since SQL specifies that tuples with null WHERE
+ * results do not get selected.
+ */
+ExprState *
+ExecInitQual(List *qual, PlanState *parent)
+{
+ ExprState *state = makeNode(ExprState);
+ ExprEvalStep scratch;
+ ListCell *lc;
+ List *adjust_bailout = NIL;
+
+ /* short-circuit (here and in ExecQual) for empty restriction list */
+ if (qual == NULL)
+ return NULL;
+
+ Assert(IsA(qual, List));
+
+ /*
+ * ExecQual() needs to return false for expression returning NULL. That
+ * allows to short-circuit the evaluation the first time a NULL is
+ * encountered. As qual evaluation is a hot-path this warrants using a
+ * special opcode for qual evaluation that's simpler than BOOL_AND (which
+ * has more complex NULL handling).
+ */
+ state->expr = (Expr *) qual;
+ ExecInitExprSlots(state, (Node *) qual);
+
+ scratch.opcode = EEO_QUAL;
+ scratch.resvalue = &state->resvalue;
+ scratch.resnull = &state->resnull;
+
+ foreach(lc, qual)
+ {
+ Expr *node = (Expr *) lfirst(lc);
+
+ /* first evaluate expression */
+ ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+ /* then check whether it's false or NULL */
+ scratch.d.qualexpr.jumpdone = -1;
+ ExprEvalPushStep(state, &scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ }
+
+ /* adjust early bail out jump target */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->d.qualexpr.jumpdone == -1);
+ as->d.qualexpr.jumpdone = state->steps_len;
+ }
+
+ scratch.opcode = EEO_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecInstantiateExpr(state);
+
+ return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual
+ * list) that 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, then the result of the
+ * conjunction is true, since SQL specifies that NULL constraint conditions
+ * are not failures.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+ Expr *expr;
+
+ if (qual == NULL)
+ return NULL;
+
+ Assert(IsA(qual, List));
+
+ if (list_length(qual) == 1)
+ expr = linitial(qual);
+ else
+ {
+ /*
+ * Just whip-up a boolean AND expression, that behaves just as needed.
+ * It'd be valid to implement short-circuiting behaviour on NULLs, but
+ * that doesn't seem warranted.
+ */
+ expr = makeBoolExpr(AND_EXPR, qual, -1);
+ }
+
+ return ExecInitExpr(expr, parent);
+}
+
+/* ----------------
+ * ExecBuildProjectionInfo
+ *
+ * Build a ProjectionInfo node for evaluating the given tlist in the given
+ * econtext, and storing the result into the tuple slot. (Caller must have
+ * ensured that tuple slot has a descriptor matching the tlist!) Note that
+ * the given tlist should be a list of ExprState nodes, not Expr nodes.
+ *
+ * inputDesc can be NULL, but if it is not, we check to see whether simple
+ * Vars in the tlist match the descriptor. It is important to provide
+ * inputDesc for relation-scan plan nodes, as a cross check that the relation
+ * hasn't been changed since the plan was made. At higher levels of a plan,
+ * there is no need to recheck.
+ *
+ * This is implemented by internally building an ExprState that performs the
+ * projection. That way faster implementations of expression evaluation, e.g
+ * compiled to native code, can evaluate the whole projection in one go.
+ * ----------------
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+ ExprContext *econtext,
+ TupleTableSlot *slot,
+ PlanState *parent,
+ TupleDesc inputDesc)
+{
+ ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+ ExprEvalStep scratch;
+ ListCell *lc;
+ ExprState *state;
+
+ projInfo->pi_exprContext = econtext;
+ projInfo->pi_state.tag.type = T_ExprState;
+ state = &projInfo->pi_state;
+ state->expr = (Expr *) targetList;
+ state->resultslot = slot;
+ ExecInitExprSlots(state, (Node *) targetList);
+
+ foreach(lc, targetList)
+ {
+ TargetEntry *tle;
+ Var *variable = NULL;
+ AttrNumber attnum;
+ bool isSimpleVar = false;
+
+ Assert(IsA(lfirst(lc), TargetEntry));
+
+ tle = (TargetEntry *) lfirst(lc);
+
+ if (tle->expr != NULL &&
+ IsA(tle->expr, Var) &&
+ ((Var *) tle->expr)->varattno > 0)
+ {
+ variable = (Var *) tle->expr;
+ attnum = variable->varattno;
+
+ if (!inputDesc)
+ isSimpleVar = true; /* can't check type, assume OK */
+ else if (variable->varattno <= inputDesc->natts)
+ {
+ Form_pg_attribute attr;
+
+ attr = inputDesc->attrs[variable->varattno - 1];
+ if (!attr->attisdropped && variable->vartype == attr->atttypid)
+ isSimpleVar = true;
+ }
+ }
+
+ if (isSimpleVar)
+ {
+ switch (variable->varno)
+ {
+ case INNER_VAR: /* get the tuple from the inner node */
+ scratch.opcode = EEO_ASSIGN_INNER_VAR;
+ break;
+
+ case OUTER_VAR: /* get the tuple from the outer node */
+ scratch.opcode = EEO_ASSIGN_OUTER_VAR;
+ break;
+
+ /* INDEX_VAR is handled by default case */
+ default: /* get the tuple from the relation being
+ * scanned */
+ scratch.opcode = EEO_ASSIGN_SCAN_VAR;
+ break;
+ }
+
+ scratch.d.assign_var.attnum = attnum - 1;
+ scratch.d.assign_var.resultnum = tle->resno - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ else
+ {
+ /*
+ * We can't directly point into the result slot for the contained
+ * expression, as the result slot (and the exprstate for that
+ * matter) can change below us. So we instead evaluate into a
+ * temporary value and then move.
+ */
+ ExecInitExprRec(tle->expr, parent, state, &state->resvalue, &state->resnull);
+ if (get_typlen(exprType((Node *) tle->expr)) == -1)
+ scratch.opcode = EEO_ASSIGN_TMP_UNEXPAND;
+ else
+ scratch.opcode = EEO_ASSIGN_TMP;
+ scratch.d.assign_tmp.resultnum = tle->resno - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ }
+
+ scratch.resvalue = &state->resvalue;
+ scratch.resnull = &state->resnull;
+ scratch.opcode = EEO_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecInstantiateExpr(state);
+
+ return projInfo;
+}
+
+/*
+ * Call ExecInitExpr() on a list of expressions, return a list of ExprStates.
+ */
+List *
+ExecInitExprList(List *nodes, PlanState *parent)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, nodes)
+ {
+ Expr *e = lfirst(lc);
+
+ result = lappend(result, ExecInitExpr(e, parent));
+ }
+
+ return result;
+}
+
+/*
+ * 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;
+}
+
+/*
+ * ExecPrepareQual --- initialize for qual 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 *
+ExecPrepareQual(List *qual, EState *estate)
+{
+ ExprState *result;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ qual = (List *) expression_planner((Expr *) qual);
+
+ result = ExecInitQual(qual, NULL);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return result;
+}
+
+/*
+ * ExecPrepareCheck -- initialize qual for execution outside a normal Plan
+ * tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitQual() for details.
+ */
+ExprState *
+ExecPrepareCheck(List *qual, EState *estate)
+{
+ ExprState *result;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt);
+
+ qual = (List *) expression_planner((Expr *) qual);
+
+ result = ExecInitCheck(qual, NULL);
+
+ MemoryContextSwitchTo(oldcontext);
+
+ return result;
+}
+
+/*
+ * Call ExecPrepareExpr() on each member of nodes, and return list of
+ * ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, nodes)
+ {
+ Expr *e = lfirst(lc);
+
+ result = lappend(result, ExecPrepareExpr(e, estate));
+ }
+
+ return result;
+}
+
+/*
+ * ExecCheck - evaluate a check constraint prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck).
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+ bool isnull;
+ Datum ret;
+
+ ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+ if (isnull)
+ return true;
+ return DatumGetBool(ret);
+}
+
+/*
+ * Prepare an expression for execution. This has to be called for every
+ * ExprState before it can be executed.
+ *
+ * NB: While this currently only calls ExecInstantiateInterpretedExpr(), this
+ * will likely get extended to further expression evaluation methods.
+ * Therefore this should be used instead of ExecInstantiateInterpretedExpr().
+ */
+static void
+ExecInstantiateExpr(ExprState *state)
+{
+ ExecInstantiateInterpretedExpr(state);
+}
+
+/*
+ * Append evaluation of node to ExprState, possibly recursing into
+ * sub-expressions of node.
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+ ExprEvalStep scratch;
+
+ /*
+ * Guard against stack overflow due to overly complex expressions. Because
+ * expression evaluation is not recursive, but expression planning is,
+ * we'll hit stack limits due to recursion either here, or when recursing
+ * into a separate expression evaluation inside a function call. This
+ * lets us avoid repeatedly doing the quite expensive stack depth checks
+ * during expression evaluation.
+ */
+ check_stack_depth();
+
+ Assert(resv != NULL && resnull != NULL);
+ scratch.resvalue = resv;
+ scratch.resnull = resnull;
+
+ /* cases should be ordered as they are in enum NodeTag */
+ switch (nodeTag(node))
+ {
+ case T_Var:
+ {
+ Var *variable = (Var *) node;
+
+ /* varattno == InvalidAttrNumber means it's a whole-row Var */
+ if (variable->varattno == InvalidAttrNumber)
+ {
+ ExecInitWholeRowVar(&scratch, variable, parent, state, resv, resnull);
+
+ scratch.opcode = EEO_WHOLEROW;
+ }
+ else if (variable->varattno <= 0)
+ {
+ scratch.d.var.attnum = variable->varattno;
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ scratch.opcode = EEO_INNER_SYSVAR;
+ break;
+ case OUTER_VAR:
+ scratch.opcode = EEO_OUTER_SYSVAR;
+ break;
+ default:
+ scratch.opcode = EEO_SCAN_SYSVAR;
+ break;
+ }
+ }
+ else
+ {
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ scratch.opcode = EEO_INNER_VAR;
+ break;
+ case OUTER_VAR:
+ scratch.opcode = EEO_OUTER_VAR;
+ break;
+ default:
+ scratch.opcode = EEO_SCAN_VAR;
+ break;
+ }
+ scratch.d.var.attnum = variable->varattno - 1;
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_Const:
+ {
+ Const *con = (Const *) node;
+
+ scratch.opcode = EEO_CONST;
+ scratch.d.constval.value = con->constvalue;
+ scratch.d.constval.isnull = con->constisnull;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_Param:
+ {
+ Param *param = (Param *) node;
+
+ switch (param->paramkind)
+ {
+ case PARAM_EXEC:
+ {
+ scratch.opcode = EEO_PARAM_EXEC;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = InvalidOid;
+ break;
+ }
+ case PARAM_EXTERN:
+ {
+ scratch.opcode = EEO_PARAM_EXTERN;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ break;
+ }
+ default:
+ elog(ERROR, "unrecognized paramkind: %d",
+ (int) ((Param *) node)->paramkind);
+ break;
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_Aggref:
+ {
+ Aggref *aggref = (Aggref *) node;
+ AggrefExprState *astate = makeNode(AggrefExprState);
+
+ scratch.opcode = EEO_AGGREF;
+ scratch.d.aggref.astate = astate;
+ astate->aggref = aggref;
+ 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");
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_GroupingFunc:
+ {
+ GroupingFunc *grp_node = (GroupingFunc *) node;
+ Agg *agg = NULL;
+
+ if (!parent || !IsA(parent, AggState) ||!IsA(parent->plan, Agg))
+ elog(ERROR, "parent of GROUPING is not Agg node");
+
+ scratch.opcode = EEO_GROUPING_FUNC;
+ scratch.d.grouping_func.parent = (AggState *) parent;
+
+ agg = (Agg *) (parent->plan);
+
+ if (agg->groupingSets)
+ scratch.d.grouping_func.clauses = grp_node->cols;
+ else
+ scratch.d.grouping_func.clauses = NIL;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_WindowFunc:
+ {
+ WindowFunc *wfunc = (WindowFunc *) node;
+ WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
+
+ wfstate->wfunc = wfunc;
+
+ 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++;
+
+ /* for now intialize agg using old style expressions */
+ wfstate->args = NIL;
+ wfstate->args = ExecInitExprList(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");
+ }
+
+ scratch.opcode = EEO_WINDOW_FUNC;
+ scratch.d.window_func.wfstate = wfstate;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ArrayRef:
+ {
+ ArrayRef *aref = (ArrayRef *) node;
+
+ ExecInitArrayRef(&scratch, aref, parent, state, resv, resnull);
+ break;
+ }
+
+ case T_FuncExpr:
+ {
+ FuncExpr *func = (FuncExpr *) node;
+
+ ExecInitFunc(&scratch, node, func->args, func->funcid, func->inputcollid,
+ parent, state, resv, resnull);
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_OpExpr:
+ {
+ OpExpr *op = (OpExpr *) node;
+
+ ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+ parent, state, resv, resnull);
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_DistinctExpr:
+ {
+ DistinctExpr *op = (DistinctExpr *) node;
+
+ ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+ parent, state, resv, resnull);
+
+ /*
+ * Can't use normal function call, override opcode for
+ * DISTINCT
+ */
+
+ /*
+ * XXX: historically we've not called the function usage
+ * pgstat infrastructure - that seems inconsistent given that
+ * we do so for normal function *and* operator evaluation
+ */
+ scratch.opcode = EEO_DISTINCT;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_NullIfExpr:
+ {
+ NullIfExpr *op = (NullIfExpr *) node;
+
+ ExecInitFunc(&scratch, node, op->args, op->opfuncid, op->inputcollid,
+ parent, state, resv, resnull);
+
+ /* Can't use normal function call, override opcode for NULL() */
+
+ /*
+ * XXX: historically we've not called the function usage
+ * pgstat infrastructure - that seems inconsistent given that
+ * we do so for normal function *and* operator evaluation
+ */
+ scratch.opcode = EEO_NULLIF;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fmgr_info(opexpr->opfuncid, finfo);
+ fmgr_info_set_expr((Node *) node, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 2,
+ opexpr->inputcollid, NULL, NULL);
+
+ scratch.opcode = EEO_SCALARARRAYOP;
+ scratch.d.scalararrayop.opexpr = opexpr;
+ scratch.d.scalararrayop.finfo = finfo;
+ scratch.d.scalararrayop.fcinfo_data = fcinfo;
+ scratch.d.scalararrayop.fn_addr = fcinfo->flinfo->fn_addr;
+
+ Assert(fcinfo->nargs == 2);
+ Assert(list_length(opexpr->args) == 2);
+
+ /* evaluate scalar directly into function argument */
+ ExecInitExprRec((Expr *) linitial(opexpr->args), parent, state,
+ &fcinfo->arg[0], &fcinfo->argnull[0]);
+
+ /*
+ * Evaluate array argument into our return value, overwrite
+ * with comparison results afterwards.
+ */
+ ExecInitExprRec((Expr *) lsecond(opexpr->args), parent, state,
+ resv, resnull);
+
+ scratch.d.scalararrayop.element_type = InvalidOid;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_BoolExpr:
+ {
+ BoolExpr *boolexpr = (BoolExpr *) node;
+ ListCell *lc;
+ List *adjust_bailout = NIL;
+ int nargs = list_length(boolexpr->args);
+ int off = 0;
+
+ /* allocate scratch memory used by all steps */
+ scratch.d.boolexpr.value = palloc0(sizeof(Datum));
+ scratch.d.boolexpr.isnull = palloc0(sizeof(bool));
+ scratch.d.boolexpr.anynull = palloc0(sizeof(bool));
+
+ /*
+ * For each argument evaluate the argument itself, then
+ * perform the bool operation's appropriate handling.
+ */
+ foreach(lc, boolexpr->args)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+
+ switch (boolexpr->boolop)
+ {
+ case AND_EXPR:
+ Assert(list_length(boolexpr->args) >= 2);
+
+ if (off == 0)
+ scratch.opcode = EEO_BOOL_AND_STEP_FIRST;
+ else if (off + 1 == nargs)
+ scratch.opcode = EEO_BOOL_AND_STEP_LAST;
+ else
+ scratch.opcode = EEO_BOOL_AND_STEP;
+ break;
+ case OR_EXPR:
+ Assert(list_length(boolexpr->args) >= 2);
+
+ if (off == 0)
+ scratch.opcode = EEO_BOOL_OR_STEP_FIRST;
+ else if (off + 1 == nargs)
+ scratch.opcode = EEO_BOOL_OR_STEP_LAST;
+ else
+ scratch.opcode = EEO_BOOL_OR_STEP;
+ break;
+ case NOT_EXPR:
+ Assert(list_length(boolexpr->args) == 1);
+
+ scratch.opcode = EEO_BOOL_NOT_STEP;
+ break;
+ default:
+ elog(ERROR, "unrecognized boolop: %d",
+ (int) boolexpr->boolop);
+ break;
+ }
+
+ ExecInitExprRec(arg, parent, state,
+ scratch.d.boolexpr.value,
+ scratch.d.boolexpr.isnull);
+ scratch.d.boolexpr.jumpdone = -1;
+ ExprEvalPushStep(state, &scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ off++;
+ }
+
+ /* adjust early bail out jump target */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->d.boolexpr.jumpdone == -1);
+ as->d.boolexpr.jumpdone = state->steps_len;
+ }
+
+ 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);
+
+ scratch.opcode = EEO_SUBPLAN;
+ scratch.d.subplan.sstate = sstate;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_AlternativeSubPlan:
+ {
+ AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+ AlternativeSubPlanState *asstate;
+
+ if (!parent)
+ elog(ERROR, "AlternativeSubPlan found with no parent plan");
+
+ asstate = ExecInitAlternativeSubPlan(asplan, parent);
+
+ scratch.opcode = EEO_ALTERNATIVE_SUBPLAN;
+ scratch.d.alternative_subplan.asstate = asstate;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_FieldSelect:
+ {
+ FieldSelect *fselect = (FieldSelect *) node;
+
+
+ /* evaluate argument */
+ ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+ scratch.opcode = EEO_FIELDSELECT;
+ scratch.d.fieldselect.fieldnum = fselect->fieldnum;
+ scratch.d.fieldselect.resulttype = fselect->resulttype;
+ scratch.d.fieldselect.argdesc = NULL;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_FieldStore:
+ {
+ FieldStore *fstore = (FieldStore *) node;
+ ListCell *l1,
+ *l2;
+ Datum *values;
+ bool *nulls;
+ TupleDesc *descp;
+
+ /* FIXME: properly size workspace */
+ values = (Datum *) palloc(sizeof(Datum) * MaxTupleAttributeNumber);
+ nulls = (bool *) palloc(sizeof(bool) * MaxTupleAttributeNumber);
+ descp = (TupleDesc *) palloc(sizeof(TupleDesc));
+ *descp = NULL;
+
+ /* prepare argument evaluation */
+ ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+ /* first deform the input tuple */
+ scratch.opcode = EEO_FIELDSTORE_DEFORM;
+ scratch.d.fieldstore.argdesc = descp;
+ scratch.d.fieldstore.fstore = fstore;
+ scratch.d.fieldstore.values = values;
+ scratch.d.fieldstore.nulls = nulls;
+ ExprEvalPushStep(state, &scratch);
+
+ /* evaluate new values, one step for each arg */
+ forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+ {
+ Expr *e = (Expr *) lfirst(l1);
+ AttrNumber fieldnum = lfirst_int(l2);
+ Datum *save_innermost_caseval = NULL;
+ bool *save_innermost_casenull = NULL;
+
+ /*
+ * 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.)
+ */
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ state->innermost_caseval = &values[fieldnum - 1];
+ state->innermost_casenull = &nulls[fieldnum - 1];
+
+ ExecInitExprRec(e, parent, state,
+ &values[fieldnum - 1],
+ &nulls[fieldnum - 1]);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+ }
+
+ /* then form result tuple */
+ scratch.opcode = EEO_FIELDSTORE_FORM;
+ scratch.d.fieldstore.fstore = fstore;
+ scratch.d.fieldstore.argdesc = descp;
+ scratch.d.fieldstore.values = values;
+ scratch.d.fieldstore.nulls = nulls;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_RelabelType:
+ {
+ /* at runtime relabel doesn't need to do anything */
+ RelabelType *relabel = (RelabelType *) node;
+
+ ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+ break;
+ }
+
+ case T_CoerceViaIO:
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ Oid iofunc;
+ bool typisvarlena;
+
+ /* evaluate argument */
+ ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+ /*
+ * Compute coercion by preparing both output / input calls, to
+ * be evaluated inside a single evaluation step for speed -
+ * this can be a very common operation.
+ */
+ scratch.opcode = EEO_IOCOERCE;
+
+ /* lookup the input type's output function */
+ scratch.d.iocoerce.finfo_out = palloc0(sizeof(*scratch.d.iocoerce.finfo_out));
+ scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_out));
+
+ getTypeOutputInfo(exprType((Node *) iocoerce->arg),
+ &iofunc, &typisvarlena);
+ fmgr_info(iofunc, scratch.d.iocoerce.finfo_out);
+ fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_out);
+ InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_out,
+ scratch.d.iocoerce.finfo_out,
+ 1, InvalidOid, NULL, NULL);
+
+ /* lookup the result type's input function */
+ scratch.d.iocoerce.finfo_in = palloc0(sizeof(*scratch.d.iocoerce.finfo_in));
+ scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(*scratch.d.iocoerce.fcinfo_data_in));
+
+ getTypeInputInfo(iocoerce->resulttype, &iofunc,
+ &scratch.d.iocoerce.intypioparam);
+ fmgr_info(iofunc, scratch.d.iocoerce.finfo_in);
+ fmgr_info_set_expr((Node *) node, scratch.d.iocoerce.finfo_in);
+ InitFunctionCallInfoData(*scratch.d.iocoerce.fcinfo_data_in,
+ scratch.d.iocoerce.finfo_in,
+ 3, InvalidOid, NULL, NULL);
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+
+ /* evaluate argument */
+ ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+ scratch.opcode = EEO_ARRAYCOERCE;
+ scratch.d.arraycoerce.coerceexpr = acoerce;
+ scratch.d.arraycoerce.resultelemtype =
+ get_element_type(acoerce->resulttype);
+ if (scratch.d.arraycoerce.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(scratch.d.arraycoerce.resultelemtype) ==
+ scratch.d.arraycoerce.resultelemtype);
+ scratch.d.arraycoerce.elemfunc =
+ (FmgrInfo *) palloc(sizeof(FmgrInfo));
+ scratch.d.arraycoerce.elemfunc->fn_oid =
+ InvalidOid; /* not initialized */
+ scratch.d.arraycoerce.amstate =
+ (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+ /* evaluate argument */
+ ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+ /* and push conversion step */
+ scratch.opcode = EEO_CONVERT_ROWTYPE;
+ scratch.d.convert_rowtype.convert = convert;
+ scratch.d.convert_rowtype.indesc = NULL;
+ scratch.d.convert_rowtype.outdesc = NULL;
+ scratch.d.convert_rowtype.map = NULL;
+ scratch.d.convert_rowtype.initialized = false;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ /* note that CaseWhen expressions are handled within this block */
+ case T_CaseExpr:
+ {
+ CaseExpr *caseExpr = (CaseExpr *) node;
+ ListCell *clause;
+ List *adjust_bailout = NIL;
+ ListCell *lc;
+ Datum *casevalue = palloc0(sizeof(Datum));
+ bool *caseisnull = palloc0(sizeof(bool));
+ Datum *save_innermost_caseval = NULL;
+ bool *save_innermost_casenull = NULL;
+ Datum *caseval = NULL;
+ bool *casenull = NULL;
+
+ /* arg == NULL -> CASE WHEN foo */
+ /* arg != NULL -> CASE foo WHEN blarg */
+ if (caseExpr->arg != NULL)
+ {
+ caseval = palloc0(sizeof(Datum));
+ casenull = palloc0(sizeof(bool));
+
+ ExecInitExprRec(caseExpr->arg, parent, state,
+ caseval, casenull);
+ }
+
+ /*
+ * 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 caseValue
+ * fields, in case this node is itself within a larger 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.
+ */
+
+
+ /*
+ * Prepare to evaluate each of the WHEN clauses in turn, as
+ * soon as one is true we return the corresponding result; and
+ * If none are true then we return the value of the default
+ * clause, or NULL if there is none.
+ */
+ foreach(clause, caseExpr->args)
+ {
+ CaseWhen *when = (CaseWhen *) lfirst(clause);
+ int whenstep;
+
+ /* evaluate condition */
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ state->innermost_caseval = caseval;
+ state->innermost_casenull = casenull;
+
+ ExecInitExprRec(when->expr, parent, state,
+ casevalue, caseisnull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+
+ scratch.opcode = EEO_CASE_WHEN_STEP;
+ scratch.d.casewhen.value = casevalue;
+ scratch.d.casewhen.isnull = caseisnull;
+ scratch.d.casewhen.jumpfalse = -1; /* computed later */
+ ExprEvalPushStep(state, &scratch);
+ whenstep = state->steps_len - 1;
+
+ /* evaluate result */
+ ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+ scratch.opcode = EEO_CASE_THEN_STEP;
+ scratch.d.casewhen.value = casevalue;
+ scratch.d.casewhen.isnull = caseisnull;
+ scratch.d.casethen.jumpdone = -1; /* computed later */
+ ExprEvalPushStep(state, &scratch);
+
+ /*
+ * Don't know "address" of jump target yet, compute once
+ * the whole case expression is built.
+ */
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+
+ /* adjust jump target for WHEN step, for the !match case */
+ state->steps[whenstep].d.casewhen.jumpfalse = state->steps_len;
+ }
+
+ if (caseExpr->defresult)
+ {
+ /* evaluate result, directly into result datum */
+ ExecInitExprRec(caseExpr->defresult, parent, state,
+ resv, resnull);
+ }
+ else
+ {
+ /* statically return NULL */
+ scratch.opcode = EEO_CONST;
+ scratch.d.constval.isnull = true;
+ scratch.d.constval.value = 0;
+ ExprEvalPushStep(state, &scratch);
+ }
+
+ /* adjust early bail out jump target */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->d.casethen.jumpdone == -1);
+ as->d.casethen.jumpdone = state->steps_len;
+ }
+
+ break;
+ }
+
+ case T_CaseTestExpr:
+ {
+ CaseTestExpr *casetestexpr = (CaseTestExpr *) node;
+
+ scratch.d.casetest.value = state->innermost_caseval;
+ scratch.d.casetest.isnull = state->innermost_casenull;
+
+ /* only check for extended datums if possible */
+ if (get_typlen(casetestexpr->typeId) == -1)
+ scratch.opcode = EEO_CASE_TESTVAL_UNEXPAND;
+ else
+ scratch.opcode = EEO_CASE_TESTVAL;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ArrayExpr:
+ {
+ ArrayExpr *arrayexpr = (ArrayExpr *) node;
+ int nelems = list_length(arrayexpr->elements);
+ ListCell *lc;
+ int elemoff;
+
+ /*
+ * Evaluate by computing each element, and then forming the
+ * array.
+ */
+ scratch.opcode = EEO_ARRAYEXPR;
+ scratch.d.arrayexpr.arrayexpr = arrayexpr;
+ scratch.d.arrayexpr.nelems = nelems;
+ scratch.d.arrayexpr.elemvalues =
+ (Datum *) palloc(sizeof(Datum) * nelems);
+ scratch.d.arrayexpr.elemnulls =
+ (bool *) palloc(sizeof(bool) * nelems);
+
+ /* do one-time catalog lookup for type info */
+ get_typlenbyvalalign(arrayexpr->element_typeid,
+ &scratch.d.arrayexpr.elemlength,
+ &scratch.d.arrayexpr.elembyval,
+ &scratch.d.arrayexpr.elemalign);
+
+ /* prepare to evaluate all arguments */
+ elemoff = 0;
+ foreach(lc, arrayexpr->elements)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.arrayexpr.elemvalues[elemoff],
+ &scratch.d.arrayexpr.elemnulls[elemoff]);
+ elemoff++;
+ }
+
+ /* and then to collect collect all into an array */
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_RowExpr:
+ {
+ RowExpr *rowexpr = (RowExpr *) node;
+ int nelems = list_length(rowexpr->args);
+ TupleDesc tupdesc;
+ Form_pg_attribute *attrs;
+ int i;
+ ListCell *l;
+
+ /*
+ * Evaluate by first building datums for each field, and then
+ * a final step forming the composite datum.
+ */
+ scratch.opcode = EEO_ROW;
+
+ /* space for the individual field datums */
+ scratch.d.row.elemvalues =
+ (Datum *) palloc(sizeof(Datum) * nelems);
+ scratch.d.row.elemnulls =
+ (bool *) palloc(sizeof(bool) * nelems);
+
+ /* Build tupdesc to describe result tuples */
+ if (rowexpr->row_typeid == RECORDOID)
+ {
+ /* generic record, use types of given expressions */
+ tupdesc = ExecTypeFromExprList(rowexpr->args);
+ }
+ else
+ {
+ /* it's been cast to a named type, use that */
+ tupdesc = lookup_rowtype_tupdesc_copy(rowexpr->row_typeid, -1);
+ }
+
+ scratch.d.row.tupdesc = tupdesc;
+
+ /* In either case, adopt RowExpr's column aliases */
+ ExecTypeSetColNames(tupdesc, rowexpr->colnames);
+ /* Bless the tupdesc in case it's now of type RECORD */
+ BlessTupleDesc(tupdesc);
+
+ /* Set up evaluation, skipping any deleted columns */
+ attrs = tupdesc->attrs;
+ i = 0;
+ foreach(l, rowexpr->args)
+ {
+ Expr *e = (Expr *) lfirst(l);
+
+ 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);
+ }
+
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.row.elemvalues[i],
+ &scratch.d.row.elemnulls[i]);
+ i++;
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ int nopers = list_length(rcexpr->opnos);
+ List *adjust_bailout = NIL;
+ ListCell *l_left_expr,
+ *l_right_expr,
+ *l_opno,
+ *l_opfamily,
+ *l_inputcollid;
+ ListCell *lc;
+ int off;
+
+ /*
+ * Iterate over each field, prepare comparisons. To handle
+ * NULL results, prepare jumps to after the expression. If
+ * expression yields a != 0 result, jump to the final step.
+ */
+ Assert(list_length(rcexpr->largs) == nopers);
+ Assert(list_length(rcexpr->rargs) == nopers);
+
+ off = 0;
+ for (off = 0,
+ l_left_expr = list_head(rcexpr->largs),
+ l_right_expr = list_head(rcexpr->rargs),
+ l_opno = list_head(rcexpr->opnos),
+ l_opfamily = list_head(rcexpr->opfamilies),
+ l_inputcollid = list_head(rcexpr->inputcollids);
+ off < nopers;
+ off++,
+ l_left_expr = lnext(l_left_expr),
+ l_right_expr = lnext(l_right_expr),
+ l_opno = lnext(l_opno),
+ l_opfamily = lnext(l_opfamily),
+ l_inputcollid = lnext(l_inputcollid))
+ {
+ Expr *left_expr = (Expr *) lfirst(l_left_expr);
+ Expr *right_expr = (Expr *) lfirst(l_right_expr);
+ Oid opno = lfirst_oid(l_opno);
+ Oid opfamily = lfirst_oid(l_opfamily);
+ Oid inputcollid = lfirst_oid(l_inputcollid);
+ int strategy;
+ Oid lefttype;
+ Oid righttype;
+ Oid proc;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+
+ get_op_opfamily_properties(opno, opfamily, false,
+ &strategy,
+ &lefttype,
+ &righttype);
+ proc = get_opfamily_proc(opfamily,
+ lefttype,
+ righttype,
+ BTORDER_PROC);
+
+ /* Set up the primary fmgr lookup information */
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fmgr_info(proc, finfo);
+ fmgr_info_set_expr((Node *) node, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 2, inputcollid, NULL, NULL);
+
+ /*
+ * 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 thus
+ * neither does this code.
+ */
+
+ /* evaluate left and right expression directly into fcinfo */
+ ExecInitExprRec(left_expr, parent, state,
+ &fcinfo->arg[0], &fcinfo->argnull[0]);
+ ExecInitExprRec(right_expr, parent, state,
+ &fcinfo->arg[1], &fcinfo->argnull[1]);
+
+ scratch.opcode = EEO_ROWCOMPARE_STEP;
+
+ /* jump targets computed later */
+ scratch.d.rowcompare_step.jumpnull = -1;
+ scratch.d.rowcompare_step.jumpdone = -1;
+
+ scratch.d.rowcompare_step.finfo = finfo;
+ scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+ scratch.d.rowcompare_step.fn_addr = fcinfo->flinfo->fn_addr;
+
+ ExprEvalPushStep(state, &scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ }
+
+ /* and then compare the last result */
+ scratch.opcode = EEO_ROWCOMPARE_FINAL;
+ scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust early bail out jump targets */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->d.rowcompare_step.jumpdone == -1);
+ Assert(as->d.rowcompare_step.jumpnull == -1);
+
+ /* jump to comparison evaluation */
+ as->d.rowcompare_step.jumpdone = state->steps_len - 1;
+ /* jump to the following expression */
+ as->d.rowcompare_step.jumpnull = state->steps_len;
+ }
+
+ break;
+ }
+
+ case T_CoalesceExpr:
+ {
+ CoalesceExpr *coalesce = (CoalesceExpr *) node;
+ List *adjust_bailout = NIL;
+ ListCell *lc;
+
+ Assert(list_length(coalesce->args) > 0);
+
+ /*
+ * Prepare evaluation of all coalesced arguments, after each
+ * push a step that short-circuits if not null.
+ */
+ foreach(lc, coalesce->args)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ /* evaluate result, directly into result datum */
+ ExecInitExprRec(e, parent, state, resv, resnull);
+
+ /* then push step checking for NULLs */
+ scratch.opcode = EEO_COALESCE;
+ scratch.d.coalesce.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, &scratch);
+
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ }
+
+ /*
+ * No need to add a constant NULL return - we only can get to
+ * the end of the expression if a NULL already is being
+ * returned.
+ */
+
+ /* adjust early bail out jump target */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->d.coalesce.jumpdone == -1);
+ as->d.coalesce.jumpdone = state->steps_len;
+ }
+
+ break;
+ }
+
+ case T_MinMaxExpr:
+ {
+ MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
+ int nelems = list_length(minmaxexpr->args);
+ TypeCacheEntry *typentry;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+ ListCell *lc;
+ int off;
+
+ /* 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 thus neither does
+ * this code.
+ */
+ finfo = palloc0(sizeof(FmgrInfo));
+ fcinfo = palloc0(sizeof(FunctionCallInfoData));
+ fmgr_info(typentry->cmp_proc, finfo);
+ fmgr_info_set_expr((Node *) node, finfo);
+ InitFunctionCallInfoData(*fcinfo, finfo, 2,
+ minmaxexpr->inputcollid, NULL, NULL);
+
+ scratch.opcode = EEO_MINMAX;
+ /* allocate space to store arguments */
+ scratch.d.minmax.values =
+ (Datum *) palloc(sizeof(Datum) * nelems);
+ scratch.d.minmax.nulls =
+ (bool *) palloc(sizeof(bool) * nelems);
+ scratch.d.minmax.nelems = nelems;
+ scratch.d.minmax.op = minmaxexpr->op;
+
+ scratch.d.minmax.finfo = finfo;
+ scratch.d.minmax.fcinfo_data = fcinfo;
+
+ /* evaluate expressions into minmax->values/nulls */
+ off = 0;
+ foreach(lc, minmaxexpr->args)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.minmax.values[off],
+ &scratch.d.minmax.nulls[off]);
+ off++;
+ }
+
+ /* and push the final comparison */
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_SQLValueFunction:
+ {
+ SQLValueFunction *svf = (SQLValueFunction *) node;
+
+ scratch.opcode = EEO_SQLVALUEFUNCTION;
+ scratch.d.sqlvaluefunction.svf = svf;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+ ListCell *arg;
+ int nnamed = list_length(xexpr->named_args);
+ int nargs = list_length(xexpr->args);
+ int off;
+
+ scratch.opcode = EEO_XMLEXPR;
+ scratch.d.xmlexpr.xexpr = xexpr;
+
+ /* allocate space for storing all the arguments */
+ if (nnamed)
+ {
+ scratch.d.xmlexpr.named_argvalue =
+ (Datum *) palloc(sizeof(Datum) * nnamed);
+ scratch.d.xmlexpr.named_argnull =
+ (bool *) palloc(sizeof(bool) * nnamed);
+ }
+ else
+ {
+ scratch.d.xmlexpr.named_argvalue = NULL;
+ scratch.d.xmlexpr.named_argnull = NULL;
+ }
+
+ if (nargs)
+ {
+ scratch.d.xmlexpr.argvalue =
+ (Datum *) palloc(sizeof(Datum) * nargs);
+ scratch.d.xmlexpr.argnull =
+ (bool *) palloc(sizeof(bool) * nargs);
+ }
+ else
+ {
+ scratch.d.xmlexpr.argvalue = NULL;
+ scratch.d.xmlexpr.argnull = NULL;
+ }
+
+ /* prepare argument execution */
+ off = 0;
+ foreach(arg, xexpr->named_args)
+ {
+ Expr *e = (Expr *) lfirst(arg);
+
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.xmlexpr.named_argvalue[off],
+ &scratch.d.xmlexpr.named_argnull[off]);
+ off++;
+ }
+
+ off = 0;
+ foreach(arg, xexpr->args)
+ {
+ Expr *e = (Expr *) lfirst(arg);
+
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.xmlexpr.argvalue[off],
+ &scratch.d.xmlexpr.argnull[off]);
+ off++;
+ }
+
+ /* and evaluate the actual XML expression */
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_NullTest:
+ {
+ NullTest *ntest = (NullTest *) node;
+
+ if (ntest->nulltesttype == IS_NULL)
+ {
+ if (ntest->argisrow)
+ scratch.opcode = EEO_NULLTEST_ROWISNULL;
+ else
+ scratch.opcode = EEO_NULLTEST_ISNULL;
+ }
+ else if (ntest->nulltesttype == IS_NOT_NULL)
+ {
+ if (ntest->argisrow)
+ scratch.opcode = EEO_NULLTEST_ROWISNOTNULL;
+ else
+ scratch.opcode = EEO_NULLTEST_ISNOTNULL;
+ }
+ else
+ {
+ elog(ERROR, "unrecognized nulltesttype: %d",
+ (int) ntest->nulltesttype);
+ }
+
+ /* first evaluate argument */
+ ExecInitExprRec(ntest->arg, parent, state,
+ resv, resnull);
+
+ /* then push the test of that argument */
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_BooleanTest:
+ {
+ BooleanTest *btest = (BooleanTest *) node;
+
+ /*
+ * Evaluate argument, directly into result datum. That's a bit
+ * debatable, because the types will be different, but it's
+ * efficient... The evaluation step will then store an actual
+ * boolean.
+ */
+ ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+ switch (btest->booltesttype)
+ {
+ case IS_TRUE:
+ scratch.opcode = EEO_BOOLTEST_IS_TRUE;
+ break;
+ case IS_NOT_TRUE:
+ scratch.opcode = EEO_BOOLTEST_IS_NOT_TRUE;
+ break;
+ case IS_FALSE:
+ scratch.opcode = EEO_BOOLTEST_IS_FALSE;
+ break;
+ case IS_NOT_FALSE:
+ scratch.opcode = EEO_BOOLTEST_IS_NOT_FALSE;
+ break;
+ case IS_UNKNOWN:
+ scratch.opcode = EEO_BOOLTEST_IS_UNKNOWN;
+ break;
+ case IS_NOT_UNKNOWN:
+ scratch.opcode = EEO_BOOLTEST_IS_NOT_UNKNOWN;
+ break;
+ default:
+ elog(ERROR, "unrecognized booltesttype: %d",
+ (int) btest->booltesttype);
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_CoerceToDomain:
+ {
+ CoerceToDomain *ctest = (CoerceToDomain *) node;
+ Datum *save_innermost_domainval = NULL;
+ bool *save_innermost_domainnull = NULL;
+ DomainConstraintRef *constraint_ref =
+ palloc(sizeof(DomainConstraintRef));
+ ListCell *l;
+
+ scratch.d.domaincheck.resulttype = ctest->resulttype;
+ scratch.d.domaincheck.checkvalue = (Datum *) palloc(sizeof(Datum));
+ scratch.d.domaincheck.checknull = (bool *) palloc(sizeof(bool));
+
+ /* evaluate argument */
+ ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+ /*
+ * XXX: In contrast to the old implementation we're evaluating
+ * the set of to-be-checked constraints at query start - that
+ * seems perfectly sensible to me. But perhaps there's a
+ * reason the previous implementation did what it did? ISTM
+ * that was just a side-effect of using the typecache (which
+ * is longer lived than a single query).
+ */
+
+ /* Make sure we have up-to-date constraints */
+ InitDomainConstraintRef(ctest->resulttype,
+ constraint_ref,
+ CurrentMemoryContext);
+ UpdateDomainConstraintRef(constraint_ref);
+
+ /*
+ * Set up value to be returned by CoerceToDomainValue nodes.
+ * We must save and restore innermost_domainval/null fields,
+ * in case this node is itself within a check expression for
+ * another domain.
+ */
+ save_innermost_domainval = state->innermost_domainval;
+ save_innermost_domainnull = state->innermost_domainnull;
+ state->innermost_domainval = resv;
+ state->innermost_domainnull = resnull;
+
+ foreach(l, constraint_ref->constraints)
+ {
+ DomainConstraintState *con = (DomainConstraintState *) lfirst(l);
+
+ scratch.d.domaincheck.constraintname = con->name;
+
+ switch (con->constrainttype)
+ {
+ case DOM_CONSTRAINT_NOTNULL:
+ scratch.opcode = EEO_DOMAIN_NOTNULL;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ case DOM_CONSTRAINT_CHECK:
+ /* evaluate check expression value */
+ ExecInitExprRec(con->check_expr, parent, state,
+ scratch.d.domaincheck.checkvalue,
+ scratch.d.domaincheck.checknull);
+
+ /* and then check result value */
+ scratch.opcode = EEO_DOMAIN_CHECK;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ default:
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
+ break;
+ }
+ }
+
+ state->innermost_domainval = save_innermost_domainval;
+ state->innermost_domainnull = save_innermost_domainnull;
+
+ break;
+ }
+
+ case T_CoerceToDomainValue:
+ {
+ CoerceToDomainValue *domainval = (CoerceToDomainValue *) node;
+
+ /* share datastructure with case testval */
+ scratch.d.casetest.value = state->innermost_domainval;
+ scratch.d.casetest.isnull = state->innermost_domainnull;
+
+ if (get_typlen(domainval->typeId) == -1)
+ scratch.opcode = EEO_DOMAIN_TESTVAL_UNEXPAND;
+ else
+ scratch.opcode = EEO_DOMAIN_TESTVAL;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_CurrentOfExpr:
+ {
+ scratch.opcode = EEO_CURRENTOFEXPR;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ break;
+ }
+}
+
+static void
+ExprEvalPushStep(ExprState *es, ExprEvalStep *s)
+{
+ if (es->steps_alloc == 0)
+ {
+ es->steps_alloc = 16;
+ es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc);
+ }
+ else if (es->steps_alloc == es->steps_len)
+ {
+ es->steps_alloc *= 2;
+ es->steps = repalloc(es->steps,
+ sizeof(ExprEvalStep) * es->steps_alloc);
+ }
+
+ memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep));
+}
+
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
+ Oid inputcollid, PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull)
+{
+ ListCell *lc;
+ AclResult aclresult;
+ int nargs = list_length(args);
+ FunctionCallInfo fcinfo;
+ int argno;
+
+ /* Check permission to call function */
+ aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid));
+ InvokeFunctionExecuteHook(funcid);
+
+ /*
+ * 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 (nargs > 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 */
+ scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo));
+ scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data));
+
+ fcinfo = scratch->d.func.fcinfo_data;
+ fmgr_info(funcid, scratch->d.func.finfo);
+ fmgr_info_set_expr((Node *) node, scratch->d.func.finfo);
+ InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo,
+ nargs, inputcollid, NULL, NULL);
+ scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr;
+ if (scratch->d.func.finfo->fn_retset)
+ {
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+ }
+
+ argno = 0;
+ foreach(lc, args)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+
+ if (IsA(arg, Const))
+ {
+ /*
+ * Don't evaluate const arguments every round; especially
+ * interesting for constants in comparisons.
+ */
+ Const *con = (Const *) arg;
+
+ fcinfo->arg[argno] = con->constvalue;
+ fcinfo->argnull[argno] = con->constisnull;
+ }
+ else
+ {
+ ExecInitExprRec(arg, parent, state, &fcinfo->arg[argno], &fcinfo->argnull[argno]);
+ }
+ argno++;
+ }
+
+ scratch->d.func.nargs = nargs;
+
+ if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats)
+ {
+ if (scratch->d.func.finfo->fn_strict && nargs > 0)
+ scratch->opcode = EEO_FUNCEXPR_STRICT;
+ else
+ scratch->opcode = EEO_FUNCEXPR;
+ }
+ else
+ {
+ if (scratch->d.func.finfo->fn_strict && nargs > 0)
+ scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE;
+ else
+ scratch->opcode = EEO_FUNCEXPR_FUSAGE;
+ }
+}
+
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+ ExprEvalStep scratch;
+ int last_outer = -1;
+ int last_inner = -1;
+ int last_scan = -1;
+
+ /*
+ * Figure out which attributes we're going to need.
+ */
+ ExecGetLastAttnums((Node *) node,
+ &last_outer,
+ &last_inner,
+ &last_scan);
+ if (last_inner > 0)
+ {
+ scratch.opcode = EEO_INNER_FETCHSOME;
+ scratch.d.fetch.last_var = last_inner;
+ ExprEvalPushStep(state, &scratch);
+ }
+ if (last_outer > 0)
+ {
+ scratch.opcode = EEO_OUTER_FETCHSOME;
+ scratch.d.fetch.last_var = last_outer;
+ ExprEvalPushStep(state, &scratch);
+ }
+ if (last_scan > 0)
+ {
+ scratch.opcode = EEO_SCAN_FETCHSOME;
+ scratch.d.fetch.last_var = last_scan;
+ ExprEvalPushStep(state, &scratch);
+ }
+}
+
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent, ExprState *state, Datum *resv, bool *resnull)
+{
+ scratch->d.wholerow.tupdesc = NULL;
+ scratch->d.wholerow.junkFilter = NULL;
+ scratch->d.wholerow.var = variable;
+ scratch->d.wholerow.first = true;
+
+ /*
+ * 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 (parent)
+ {
+ PlanState *subplan = NULL;
+
+ switch (nodeTag(parent))
+ {
+ case T_SubqueryScanState:
+ subplan = ((SubqueryScanState *) parent)->subplan;
+ break;
+ case T_CteScanState:
+ subplan = ((CteScanState *) 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 (junk_filter_needed)
+ {
+ /* If so, build the junkfilter in the query memory context */
+ scratch->d.wholerow.junkFilter =
+ ExecInitJunkFilter(subplan->plan->targetlist,
+ ExecGetResultType(subplan)->tdhasoid,
+ ExecInitExtraTupleSlot(parent->state));
+ }
+ }
+ }
+}
+
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+ ExprState *state, Datum *resv, bool *resnull)
+{
+ List *adjust_bailout = NIL;
+ ListCell *lc;
+ bool isAssignment = (aref->refassgnexpr != NULL);
+ ArrayRefState *arefstate = palloc(sizeof(ArrayRefState));
+ int i;
+
+ if (list_length(aref->refupperindexpr) >= MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ list_length(aref->refupperindexpr), MAXDIM)));
+
+ if (list_length(aref->reflowerindexpr) >= MAXDIM)
+ ereport(ERROR,
+ (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+ errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ list_length(aref->refupperindexpr), MAXDIM)));
+
+ if (list_length(aref->reflowerindexpr) > 0 &&
+ list_length(aref->refupperindexpr) != list_length(aref->reflowerindexpr))
+ elog(ERROR, "upper and lower index lists are not same length");
+
+ arefstate->isassignment = isAssignment;
+ arefstate->refattrlength = get_typlen(aref->refarraytype);
+ arefstate->refelemtype = aref->refelemtype;
+ get_typlenbyvalalign(aref->refelemtype,
+ &arefstate->refelemlength,
+ &arefstate->refelembyval,
+ &arefstate->refelemalign);
+
+ /* evaluate input */
+ ExecInitExprRec(aref->refexpr, parent, state,
+ resv, resnull);
+
+ /*
+ * If refexpr yields NULL, and it's a fetch, then result is NULL. In the
+ * assignment case, we'll create an empty input.
+ */
+ if (!isAssignment)
+ {
+ scratch->opcode = EEO_ARRAYREF_CHECKINPUT;
+ scratch->d.arrayref.state = arefstate;
+ scratch->d.arrayref.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ }
+
+ /* evaluate upper subscripts */
+ i = 0;
+ foreach(lc, aref->refupperindexpr)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ if (!e)
+ {
+ arefstate->upperprovided[i] = false;
+ i++;
+ continue;
+ }
+
+ arefstate->upperprovided[i] = true;
+
+ ExecInitExprRec(e, parent, state,
+ &arefstate->upper[i], &arefstate->uppernull[i]);
+
+ scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+ scratch->d.arrayref_checksubscript.state = arefstate;
+ scratch->d.arrayref_checksubscript.off = i;
+ scratch->d.arrayref_checksubscript.isupper = true;
+ scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ i++;
+ }
+ arefstate->numupper = i;
+
+ /* evaluate lower subscripts */
+ i = 0;
+ foreach(lc, aref->reflowerindexpr)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ if (!e)
+ {
+ arefstate->lowerprovided[i] = false;
+ i++;
+ continue;
+ }
+
+ arefstate->lowerprovided[i] = true;
+
+ ExecInitExprRec(e, parent, state,
+ &arefstate->lower[i], &arefstate->lowernull[i]);
+
+ scratch->opcode = EEO_ARRAYREF_CHECKSUBSCRIPT;
+ scratch->d.arrayref_checksubscript.state = arefstate;
+ scratch->d.arrayref_checksubscript.off = i;
+ scratch->d.arrayref_checksubscript.isupper = false;
+ scratch->d.arrayref_checksubscript.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_bailout = lappend_int(adjust_bailout,
+ state->steps_len - 1);
+ i++;
+ }
+ arefstate->numlower = i;
+
+ if (isAssignment)
+ {
+ Datum *save_innermost_caseval = NULL;
+ bool *save_innermost_casenull = NULL;
+
+ /*
+ * 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.
+ */
+ if (isAssignmentIndirectionExpr(aref->refassgnexpr))
+ {
+ scratch->opcode = EEO_ARRAYREF_OLD;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ state->innermost_caseval = &arefstate->prevvalue;
+ state->innermost_casenull = &arefstate->prevnull;
+
+ /* evaluate replacement value */
+ ExecInitExprRec(aref->refassgnexpr, parent, state,
+ &arefstate->replacevalue, &arefstate->replacenull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+
+ scratch->opcode = EEO_ARRAYREF_ASSIGN;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+ else
+ {
+ scratch->opcode = EEO_ARRAYREF_FETCH;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ /* adjust early bail out jump target */
+ foreach(lc, adjust_bailout)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ if (as->opcode == EEO_ARRAYREF_CHECKSUBSCRIPT)
+ {
+ Assert(as->d.arrayref_checksubscript.jumpdone == -1);
+ as->d.arrayref_checksubscript.jumpdone = state->steps_len;
+ }
+ else
+ {
+ Assert(as->d.arrayref.jumpdone == -1);
+ as->d.arrayref.jumpdone = state->steps_len;
+ }
+ }
+}
+
+/*
+ * Helper for preparing ArrayRef expressions for evaluation: is expr a nested
+ * FieldStore or ArrayRef that might need the old element value passed down?
+ *
+ * (We could use this in FieldStore too, but in that case passing the old
+ * value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(Expr *expr)
+{
+ if (expr == NULL)
+ return false; /* just paranoia */
+ if (IsA(expr, FieldStore))
+ {
+ FieldStore *fstore = (FieldStore *) expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(expr, ArrayRef))
+ {
+ ArrayRef *arrayRef = (ArrayRef *) expr;
+
+ if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
+ return true;
+ }
+ return false;
+}
/* Check for partial index */
if (indexInfo->ii_Predicate != NIL)
{
- List *predicate;
+ ExprState *predicate;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
- if (predicate == NIL)
+ if (predicate == NULL)
{
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
indexInfo->ii_PredicateState = predicate;
}
/* Skip this index-update if the predicate isn't satisfied */
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
/* Check for partial index */
if (indexInfo->ii_Predicate != NIL)
{
- List *predicate;
+ ExprState *predicate;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
- if (predicate == NIL)
+ if (predicate == NULL)
{
- predicate = (List *)
- ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
- estate);
+ predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
indexInfo->ii_PredicateState = predicate;
}
/* Skip this index-update if the predicate isn't satisfied */
- if (!ExecQual(predicate, econtext, false))
+ if (!ExecQual(predicate, econtext))
continue;
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execInterpExpr.c
+ * Opcode based expression evaluation.
+ *
+ * This file provides a "switch threaded" (all compilers) and "direct
+ * threaded" (gcc, clang and compatible) implementation of expression
+ * evaluation. The former is among st the fastest known methods of
+ * interpreting programs without resorting to assembly level work, or
+ * just-in-time compilation, but requires support for computed gotos. The
+ * latter is amongst the fastest approaches doable in standard C.
+ *
+ * Both work by using ExprEvalStep->opcode to dispatch into code blocks
+ * implementing the specific method. Switch based uses a plain switch()
+ * statement to perform the dispatch. This has the advantages of being plain C
+ * and allowing to warn if implementation of a specific opcode has been
+ * forgotten. The disadvantage is that dispatches will, as commonly
+ * implemented by compilers, happen from a single location, causing bad branch
+ * prediction. Direct dispatch uses the label-as-values gcc extension - also
+ * adopted by some other compilers - to replace ExprEvalStep-> opcode with the
+ * address of the block implementing the instruction. This allows for better
+ * branch prediction (the jumps are happening from different locations) and
+ * fewer jumps (no jumps to common dispatch location needed).
+ *
+ * If computed gotos are available, ExecInstantiateInterpretedExpr will
+ * replace ->opcode with the address of relevant code-block and
+ * ExprState->flags will contain EEO_FLAG_JUMP_THREADED to "remember" that
+ * fact.
+ *
+ * For very simple instructions the overhead of the full interpreter
+ * "startup", as minimal as it is, is noticeable. Therefore
+ * ExecInstantiateInterpretedExpr() will choose to implement simple scalar Var
+ * and Const expressions using special cased routines (ExecJust*).
+ * Benchmarking shows anything more complex than those will benefit from the
+ * "full interpreter".
+ *
+ * Complex or uncommon instructions are not implemented in ExecInterpExpr. For
+ * one, there'd not be a noticeable performance benefit, but more importantly
+ * those complex routines are intended to be shared between different
+ * expression evaluation approaches.
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/execInterprExpr.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#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 "executor/execExpr.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parsetree.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/date.h"
+#include "utils/lsyscache.h"
+#include "utils/timestamp.h"
+#include "utils/typcache.h"
+#include "utils/xml.h"
+
+
+/*
+ * Use computed goto based opcode dispatch when computed gotos are
+ * available. But allow to disable locally in this file for development.
+ */
+#ifdef HAVE__COMPUTED_GOTO
+#define EEO_USE_COMPUTED_GOTO
+#endif /* HAVE__COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+static void **dispatch_table = NULL;
+#define EEO_SWITCH(d)
+#define EEO_DISPATCH_DIRECT(op) goto *((void *) op->opcode)
+#define EEO_CASE(name) CASE_##name:
+#else
+#define EEO_SWITCH(d) switch ((ExprEvalOp) d)
+#define EEO_DISPATCH_DIRECT(op) goto starteval
+#define EEO_CASE(name) case name:
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_DISPATCH(op) \
+ do \
+ { \
+ op++; \
+ EEO_DISPATCH_DIRECT(op); \
+ } \
+ while (0)
+
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecPrepareInterp(void);
+
+/* support functions */
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+ TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+
+/* fast-path evaluation functions */
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecInstantiateInterpretedExpr(ExprState *state)
+{
+ ExecPrepareInterp();
+
+ Assert(state->steps_len >= 1);
+ Assert(ExecEvalStepOp(state, &state->steps[state->steps_len - 1]) == EEO_DONE);
+
+ /*
+ * Fast-path for very simple expressions. "starting up" the full
+ * interpreter is a measurable overhead for these. Plain Vars and Const
+ * seem to be the only ones where the intrinsic cost is small enough that
+ * the overhead of ExecInterpExpr matters. For more complex expressions
+ * it's cheaper to use ExecInterpExpr anyways.
+ */
+ if (state->steps_len == 3)
+ {
+ ExprEvalOp step0 = ExecEvalStepOp(state, &state->steps[0]);
+ ExprEvalOp step1 = ExecEvalStepOp(state, &state->steps[1]);
+
+ if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_INNER_VAR)
+ {
+ state->evalfunc = ExecJustInnerVar;
+ return;
+ }
+ else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_OUTER_VAR)
+ {
+ state->evalfunc = ExecJustOuterVar;
+ return;
+ }
+ else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_SCAN_VAR)
+ {
+ state->evalfunc = ExecJustScanVar;
+ return;
+ }
+ else if (step0 == EEO_INNER_FETCHSOME && step1 == EEO_ASSIGN_INNER_VAR)
+ {
+ state->evalfunc = ExecJustAssignInnerVar;
+ return;
+ }
+ else if (step0 == EEO_OUTER_FETCHSOME && step1 == EEO_ASSIGN_OUTER_VAR)
+ {
+ state->evalfunc = ExecJustAssignOuterVar;
+ return;
+ }
+ else if (step0 == EEO_SCAN_FETCHSOME && step1 == EEO_ASSIGN_SCAN_VAR)
+ {
+ state->evalfunc = ExecJustAssignScanVar;
+ return;
+ }
+ }
+ else if (state->steps_len == 2 &&
+ ExecEvalStepOp(state, &state->steps[0]) == EEO_CONST)
+ {
+ state->evalfunc = ExecJustConst;
+ return;
+ }
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+ /*
+ * In the jump-threaded implementation, replace opcode with the address to
+ * jump to. To reverse, use ExecEvalStepOp().
+ */
+ {
+ int off;
+
+ for (off = 0; off < state->steps_len; off++)
+ {
+ ExprEvalStep *op = &state->steps[off];
+
+ op->opcode = (size_t) dispatch_table[op->opcode];
+ }
+
+ state->flags |= EEO_FLAG_JUMP_THREADED;
+ }
+#endif /* defined(EEO_USE_COMPUTED_GOTO) */
+
+ state->evalfunc = ExecInterpExpr;
+}
+
+
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op;
+ const TupleTableSlot *resultslot;
+ TupleTableSlot *innerslot;
+ TupleTableSlot *outerslot;
+ TupleTableSlot *scanslot;
+
+ /*
+ * This array has to be in the same order as ExprEvalOp.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+ static const void **dispatch_table[] = {
+ &&CASE_EEO_DONE,
+ &&CASE_EEO_INNER_FETCHSOME,
+ &&CASE_EEO_OUTER_FETCHSOME,
+ &&CASE_EEO_SCAN_FETCHSOME,
+ &&CASE_EEO_INNER_VAR,
+ &&CASE_EEO_OUTER_VAR,
+ &&CASE_EEO_SCAN_VAR,
+ &&CASE_EEO_ASSIGN_INNER_VAR,
+ &&CASE_EEO_ASSIGN_OUTER_VAR,
+ &&CASE_EEO_ASSIGN_SCAN_VAR,
+ &&CASE_EEO_ASSIGN_TMP,
+ &&CASE_EEO_ASSIGN_TMP_UNEXPAND,
+ &&CASE_EEO_INNER_SYSVAR,
+ &&CASE_EEO_OUTER_SYSVAR,
+ &&CASE_EEO_SCAN_SYSVAR,
+ &&CASE_EEO_CONST,
+ &&CASE_EEO_FUNCEXPR,
+ &&CASE_EEO_FUNCEXPR_STRICT,
+ &&CASE_EEO_FUNCEXPR_FUSAGE,
+ &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEO_BOOL_AND_STEP_FIRST,
+ &&CASE_EEO_BOOL_AND_STEP,
+ &&CASE_EEO_BOOL_AND_STEP_LAST,
+ &&CASE_EEO_BOOL_OR_STEP_FIRST,
+ &&CASE_EEO_BOOL_OR_STEP,
+ &&CASE_EEO_BOOL_OR_STEP_LAST,
+ &&CASE_EEO_BOOL_NOT_STEP,
+ &&CASE_EEO_QUAL,
+ &&CASE_EEO_NULLTEST_ISNULL,
+ &&CASE_EEO_NULLTEST_ISNOTNULL,
+ &&CASE_EEO_NULLTEST_ROWISNULL,
+ &&CASE_EEO_NULLTEST_ROWISNOTNULL,
+ &&CASE_EEO_PARAM_EXEC,
+ &&CASE_EEO_PARAM_EXTERN,
+ &&CASE_EEO_CASE_WHEN_STEP,
+ &&CASE_EEO_CASE_THEN_STEP,
+ &&CASE_EEO_CASE_TESTVAL,
+ &&CASE_EEO_CASE_TESTVAL_UNEXPAND,
+ &&CASE_EEO_COALESCE,
+ &&CASE_EEO_BOOLTEST_IS_TRUE,
+ &&CASE_EEO_BOOLTEST_IS_NOT_TRUE,
+ &&CASE_EEO_BOOLTEST_IS_FALSE,
+ &&CASE_EEO_BOOLTEST_IS_NOT_FALSE,
+ &&CASE_EEO_BOOLTEST_IS_UNKNOWN,
+ &&CASE_EEO_BOOLTEST_IS_NOT_UNKNOWN,
+ &&CASE_EEO_WHOLEROW,
+ &&CASE_EEO_IOCOERCE,
+ &&CASE_EEO_DISTINCT,
+ &&CASE_EEO_NULLIF,
+ &&CASE_EEO_SQLVALUEFUNCTION,
+ &&CASE_EEO_CURRENTOFEXPR,
+ &&CASE_EEO_ARRAYEXPR,
+ &&CASE_EEO_ARRAYCOERCE,
+ &&CASE_EEO_ROW,
+ &&CASE_EEO_ROWCOMPARE_STEP,
+ &&CASE_EEO_ROWCOMPARE_FINAL,
+ &&CASE_EEO_MINMAX,
+ &&CASE_EEO_FIELDSELECT,
+ &&CASE_EEO_FIELDSTORE_DEFORM,
+ &&CASE_EEO_FIELDSTORE_FORM,
+ &&CASE_EEO_ARRAYREF_CHECKINPUT,
+ &&CASE_EEO_ARRAYREF_CHECKSUBSCRIPT,
+ &&CASE_EEO_ARRAYREF_OLD,
+ &&CASE_EEO_ARRAYREF_ASSIGN,
+ &&CASE_EEO_ARRAYREF_FETCH,
+ &&CASE_EEO_CONVERT_ROWTYPE,
+ &&CASE_EEO_SCALARARRAYOP,
+ &&CASE_EEO_DOMAIN_TESTVAL,
+ &&CASE_EEO_DOMAIN_TESTVAL_UNEXPAND,
+ &&CASE_EEO_DOMAIN_NOTNULL,
+ &&CASE_EEO_DOMAIN_CHECK,
+ &&CASE_EEO_XMLEXPR,
+ &&CASE_EEO_AGGREF,
+ &&CASE_EEO_GROUPING_FUNC,
+ &&CASE_EEO_WINDOW_FUNC,
+ &&CASE_EEO_SUBPLAN,
+ &&CASE_EEO_ALTERNATIVE_SUBPLAN,
+ &&CASE_EEO_LAST
+ };
+
+ StaticAssertStmt(EEO_LAST + 1 == lengthof(dispatch_table),
+ "dispatch_table out of whack with ExprEvalOp");
+#endif
+
+#ifdef EEO_USE_COMPUTED_GOTO
+ if (unlikely(state == NULL))
+ {
+ return PointerGetDatum(dispatch_table);
+ }
+#endif
+
+ /* setup state */
+ op = state->steps;
+ resultslot = state->resultslot;
+ innerslot = econtext->ecxt_innertuple;
+ outerslot = econtext->ecxt_outertuple;
+ scanslot = econtext->ecxt_scantuple;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+ EEO_DISPATCH_DIRECT(op);
+#else
+starteval:
+#endif
+ EEO_SWITCH(op->opcode)
+ {
+ EEO_CASE(EEO_DONE)
+ {
+ goto out;
+ }
+
+ EEO_CASE(EEO_INNER_FETCHSOME)
+ {
+ /* XXX: worthwhile to check tts_nvalid inline first? */
+ slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_OUTER_FETCHSOME)
+ {
+ slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SCAN_FETCHSOME)
+ {
+ slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_INNER_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /*
+ * Can't assert tts_nvalid, as wholerow var evaluation or such
+ * could have materialized the slot - but the contents are still
+ * valid :/
+ */
+ Assert(op->d.var.attnum >= 0);
+ *op->resnull = innerslot->tts_isnull[attnum];
+ *op->resvalue = innerslot->tts_values[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_OUTER_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEO_INNER_VAR comments */
+ Assert(op->d.var.attnum >= 0);
+ *op->resnull = outerslot->tts_isnull[attnum];
+ *op->resvalue = outerslot->tts_values[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SCAN_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEO_INNER_VAR comments */
+ Assert(op->d.var.attnum >= 0);
+ *op->resnull = scanslot->tts_isnull[attnum];
+ *op->resvalue = scanslot->tts_values[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ASSIGN_INNER_VAR)
+ {
+ size_t resultnum = op->d.assign_var.resultnum;
+ size_t attnum = op->d.assign_var.attnum;
+
+ resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ASSIGN_OUTER_VAR)
+ {
+ size_t resultnum = op->d.assign_var.resultnum;
+ size_t attnum = op->d.assign_var.attnum;
+
+ resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ASSIGN_SCAN_VAR)
+ {
+ size_t resultnum = op->d.assign_var.resultnum;
+ size_t attnum = op->d.assign_var.attnum;
+
+ resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ASSIGN_TMP)
+ {
+ size_t resultnum = op->d.assign_tmp.resultnum;
+
+ resultslot->tts_values[resultnum] = state->resvalue;
+ resultslot->tts_isnull[resultnum] = state->resnull;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ASSIGN_TMP_UNEXPAND)
+ {
+ size_t resultnum = op->d.assign_tmp.resultnum;
+
+ resultslot->tts_isnull[resultnum] = state->resnull;
+
+ if (!resultslot->tts_isnull[resultnum])
+ resultslot->tts_values[resultnum] =
+ MakeExpandedObjectReadOnlyInternal(state->resvalue);
+ else
+ resultslot->tts_values[resultnum] = state->resvalue;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_INNER_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ Assert(op->d.var.attnum < 0);
+ *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+ innerslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_OUTER_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ Assert(op->d.var.attnum < 0);
+ *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+ scanslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SCAN_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ Assert(op->d.var.attnum < 0);
+ *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+ scanslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_CONST)
+ {
+ *op->resnull = op->d.constval.isnull;
+ *op->resvalue = op->d.constval.value;
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Function-call implementations. Arguments have previously been
+ * evaluated directly into fcinfo->args.
+ *
+ * As both STRICT checks and function-usage are noticeable performance
+ * wise, and function calls are a very hot-path (they also back
+ * operators!), separate all of them into separate opcodes.
+ */
+ EEO_CASE(EEO_FUNCEXPR)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FUNCEXPR_STRICT)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+ int argno;
+ bool *argnull = fcinfo->argnull;
+
+ /* strict function, check for NULL args */
+ for (argno = 0; argno < op->d.func.nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+ goto strictfail;
+ }
+ }
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ strictfail:
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FUNCEXPR_FUSAGE)
+ {
+ PgStat_FunctionCallUsage fcusage;
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE)
+ {
+ PgStat_FunctionCallUsage fcusage;
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+ int argno;
+ bool *argnull = fcinfo->argnull;
+
+ /* strict function, check for NULL args */
+ for (argno = 0; argno < op->d.func.nargs; argno++)
+ {
+ if (argnull[argno])
+ {
+ *op->resnull = true;
+ goto strictfail_fusage;
+ }
+ }
+
+ pgstat_init_function_usage(fcinfo, &fcusage);
+
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+ strictfail_fusage:
+ pgstat_end_function_usage(&fcusage, true);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * 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.
+ */
+ EEO_CASE(EEO_BOOL_AND_STEP_FIRST)
+ {
+ *op->d.boolexpr.anynull = false;
+
+ /*
+ * Fallthrough (can't be last - ANDs have two arguments at least).
+ */
+ }
+
+ EEO_CASE(EEO_BOOL_AND_STEP)
+ {
+ if (*op->d.boolexpr.isnull)
+ {
+ *op->d.boolexpr.anynull = true;
+ }
+ else if (!DatumGetBool(*op->d.boolexpr.value))
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+ /* bail out early */
+ op = &state->steps[op->d.boolexpr.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOL_AND_STEP_LAST)
+ {
+ if (*op->d.boolexpr.isnull)
+ {
+ *op->resnull = true;
+ *op->resvalue = 0;
+ }
+ else if (!DatumGetBool(*op->d.boolexpr.value))
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+
+ /*
+ * No point jumping early to jumpdone - would be same target
+ * (as this is the last argument to the AND expression),
+ * except more expensive.
+ */
+ }
+ else if (*op->d.boolexpr.anynull)
+ {
+ *op->resnull = true;
+ *op->resvalue = 0;
+ }
+ else
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(true);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * 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.
+ */
+ EEO_CASE(EEO_BOOL_OR_STEP_FIRST)
+ {
+ *op->d.boolexpr.anynull = false;
+
+ /*
+ * Fallthrough (can't be last - ORs have two arguments at least).
+ */
+ }
+
+ EEO_CASE(EEO_BOOL_OR_STEP)
+ {
+ if (*op->d.boolexpr.isnull)
+ {
+ *op->d.boolexpr.anynull = true;
+ }
+ else if (DatumGetBool(*op->d.boolexpr.value))
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(true);
+
+ /* bail out early */
+ op = &state->steps[op->d.boolexpr.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOL_OR_STEP_LAST)
+ {
+ if (*op->d.boolexpr.isnull)
+ {
+ *op->resnull = true;
+ *op->resvalue = 0;
+ }
+ else if (DatumGetBool(*op->d.boolexpr.value))
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(true);
+
+ /*
+ * No point jumping to jumpdone - would be same target (as
+ * this is the last argument to the AND expression), except
+ * more expensive.
+ */
+ }
+ else if (*op->d.boolexpr.anynull)
+ {
+ *op->resnull = true;
+ *op->resvalue = 0;
+ }
+ else
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOL_NOT_STEP)
+ {
+ /*
+ * If the expression evaluates to null, then just return null
+ * back.
+ */
+ *op->resnull = *op->d.boolexpr.isnull;
+
+ /*
+ * evaluation of 'not' is simple.. expr is false, then return
+ * 'true' and vice versa.
+ */
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value));
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_QUAL)
+ {
+ /* special case for ExecQual() */
+
+ if (*op->resnull ||
+ !DatumGetBool(*op->resvalue))
+ {
+ /* bail out early */
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+ op = &state->steps[op->d.qualexpr.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_NULLTEST_ISNULL)
+ {
+ *op->resvalue = BoolGetDatum(*op->resnull);
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_NULLTEST_ISNOTNULL)
+ {
+ *op->resvalue = BoolGetDatum(!*op->resnull);
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_NULLTEST_ROWISNULL)
+ {
+ /* out of line implementation: too large */
+ ExecEvalRowNull(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_NULLTEST_ROWISNOTNULL)
+ {
+ /* out of line implementation: too large */
+ ExecEvalRowNotNull(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_PARAM_EXEC)
+ {
+ /* out of line implementation: too large */
+ ExecEvalParamExec(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_PARAM_EXTERN)
+ {
+ /* out of line implementation: too large */
+ ExecEvalParamExtern(state, op, econtext);
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_CASE_WHEN_STEP)
+ {
+ if (!*op->d.casewhen.isnull
+ && DatumGetBool(*op->d.casewhen.value))
+ {
+ EEO_DISPATCH(op);
+ }
+ else
+ {
+ op = &state->steps[op->d.casewhen.jumpfalse];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+ }
+
+ EEO_CASE(EEO_CASE_THEN_STEP)
+ {
+ /* results already placed in correct place during preceding steps */
+ op = &state->steps[op->d.casethen.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_CASE(EEO_CASE_TESTVAL)
+ {
+ /*
+ * Normally upper parts in the expression tree have setup the
+ * values to be returned here, but some parts of the system
+ * currently misuse {caseValue,domainValue}_{datum,isNull} to set
+ * run-time data. So if no values have been set-up, use
+ * ExprContext's. This isn't pretty, but also not *that* ugly,
+ * and this is unlikely to be performance sensitive enoiugh to
+ * worry about a branch.
+ */
+ if (op->d.casetest.value)
+ {
+ *op->resvalue = *op->d.casetest.value;
+ *op->resnull = *op->d.casetest.isnull;
+ }
+ else
+ {
+ *op->resvalue = econtext->caseValue_datum;
+ *op->resnull = econtext->caseValue_isNull;
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_CASE_TESTVAL_UNEXPAND)
+ {
+ /*
+ * See EEO_CASE_TESTVAL comment.
+ */
+ if (op->d.casetest.value)
+ {
+ *op->resvalue = *op->d.casetest.value;
+ *op->resnull = *op->d.casetest.isnull;
+ }
+ else
+ {
+ *op->resvalue = econtext->caseValue_datum;
+ *op->resnull = econtext->caseValue_isNull;
+ }
+
+ /* Since caseValue_datum may be read multiple times, force to R/O */
+ if (!*op->resnull)
+ {
+ *op->resvalue =
+ MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_DOMAIN_TESTVAL)
+ {
+ /*
+ * See EEO_CASE_TESTVAL comment.
+ */
+ if (op->d.casetest.value)
+ {
+ *op->resvalue = *op->d.casetest.value;
+ *op->resnull = *op->d.casetest.isnull;
+ }
+ else
+ {
+ *op->resvalue = econtext->domainValue_datum;
+ *op->resnull = econtext->domainValue_isNull;
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_DOMAIN_TESTVAL_UNEXPAND)
+ {
+ /*
+ * See EEO_CASE_TESTVAL comment.
+ */
+ if (op->d.casetest.value)
+ {
+ *op->resvalue = *op->d.casetest.value;
+ *op->resnull = *op->d.casetest.isnull;
+ }
+ else
+ {
+ *op->resvalue = econtext->domainValue_datum;
+ *op->resnull = econtext->domainValue_isNull;
+ }
+
+ /*
+ * Since domainValue_datum may be read multiple times, force to
+ * R/O
+ */
+ if (!*op->resnull)
+ {
+ *op->resvalue =
+ MakeExpandedObjectReadOnlyInternal(*op->resvalue);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Evaluate a single argument to COALESCE and jump-out if a NOT NULL
+ * one has been found.
+ */
+ EEO_CASE(EEO_COALESCE)
+ {
+ if (!*op->resnull)
+ {
+ op = &state->steps[op->d.coalesce.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ /* BooleanTest implementations for all booltesttypes */
+ EEO_CASE(EEO_BOOLTEST_IS_TRUE)
+ {
+ if (*op->resnull)
+ *op->resvalue = false;
+ else
+ *op->resvalue = *op->resvalue;
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOLTEST_IS_NOT_FALSE)
+ {
+ if (*op->resnull)
+ *op->resvalue = true;
+ else
+ *op->resvalue = *op->resvalue;
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOLTEST_IS_FALSE)
+ {
+ if (*op->resnull)
+ *op->resvalue = false;
+ else
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOLTEST_IS_NOT_TRUE)
+ {
+ if (*op->resnull)
+ *op->resvalue = true;
+ else
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOLTEST_IS_UNKNOWN)
+ {
+ *op->resvalue = BoolGetDatum(*op->resnull);
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_BOOLTEST_IS_NOT_UNKNOWN)
+ {
+ *op->resvalue = BoolGetDatum(!*op->resnull);
+ *op->resnull = false;
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_WHOLEROW)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalWholeRowVar(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Evaluate a CoerceViaIO node. This can be quite a hot path, so
+ * inline as much work as possible.
+ */
+ EEO_CASE(EEO_IOCOERCE)
+ {
+ char *str;
+
+ /* call output function (similar to OutputFunctionCall) */
+ if (*op->resnull)
+ {
+ /* output functions are not called on nulls */
+ str = NULL;
+ }
+ else
+ {
+ FunctionCallInfo fcinfo_out;
+
+ fcinfo_out = op->d.iocoerce.fcinfo_data_out;
+ fcinfo_out->arg[0] = *op->resvalue;
+ fcinfo_out->argnull[0] = false;
+
+ str = DatumGetPointer(FunctionCallInvoke(fcinfo_out));
+ Assert(!fcinfo_out->isnull);
+ }
+
+ /* call input function (similar to InputFunctionCall) */
+ if (!op->d.iocoerce.finfo_in->fn_strict || str != NULL)
+ {
+ FunctionCallInfo fcinfo_in;
+
+ fcinfo_in = op->d.iocoerce.fcinfo_data_in;
+ fcinfo_in->arg[0] = PointerGetDatum(str);
+ fcinfo_in->argnull[0] = BoolGetDatum(*op->resnull);
+ fcinfo_in->arg[1] = op->d.iocoerce.intypioparam;
+ fcinfo_in->argnull[1] = false;
+ fcinfo_in->arg[2] = -1;
+ fcinfo_in->argnull[2] = false;
+
+ fcinfo_in->isnull = false;
+ *op->resvalue = FunctionCallInvoke(fcinfo_in);
+
+ /* Should get null result if and only if str is NULL */
+ if (str == NULL)
+ {
+ Assert(*op->resnull);
+ Assert(fcinfo_in->isnull);
+ }
+ else
+ {
+ Assert(!*op->resnull);
+ Assert(!fcinfo_in->isnull);
+ }
+
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * IS DISTINCT FROM must evaluate arguments (already done into
+ * fcinfo->arg/argnull) 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 comparison 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().
+ */
+ EEO_CASE(EEO_DISTINCT)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ /* check function arguments for NULLness */
+ if (fcinfo->argnull[0] && fcinfo->argnull[1])
+ {
+ /* Both NULL? Then is not distinct... */
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+ }
+ else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+ {
+ /* Only one is NULL? Then is distinct... */
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(true);
+ }
+ else
+ {
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ /* Must invert result of "=" */
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+ *op->resnull = fcinfo->isnull;
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_NULLIF)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ /* if either argument is NULL they can't be equal */
+ if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+ {
+ bool result;
+
+ fcinfo->isnull = false;
+ result = (op->d.func.fn_addr) (fcinfo);
+
+ /* if the arguments are equal return null */
+ if (!fcinfo->isnull && DatumGetBool(result))
+ {
+ *op->resnull = true;
+ *op->resvalue = true;
+
+ EEO_DISPATCH(op);
+ }
+ }
+ *op->resnull = fcinfo->argnull[0];
+ *op->resvalue = fcinfo->arg[0];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SQLVALUEFUNCTION)
+ {
+ /*
+ * Doesn't seem worthwhile to have an inline implementation
+ * efficency-wise.
+ */
+ ExecEvalSQLValueFunction(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_CURRENTOFEXPR)
+ {
+ /* error invocation uses space, and shouldn't ever occur */
+ ExecEvalCurrentOfExpr(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ARRAYEXPR)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayExpr(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ARRAYCOERCE)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayCoerce(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ROW)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalRow(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ROWCOMPARE_STEP)
+ {
+ FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+ /* force NULL result */
+ if (op->d.rowcompare_step.finfo->fn_strict &&
+ (fcinfo->argnull[0] || fcinfo->argnull[1]))
+ {
+ *op->resnull = true;
+ op = &state->steps[op->d.rowcompare_step.jumpnull];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo);
+
+ /* force NULL result */
+ if (fcinfo->isnull)
+ {
+ *op->resnull = true;
+ op = &state->steps[op->d.rowcompare_step.jumpnull];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ /* no need to compare remaining columns */
+ if (DatumGetInt32(*op->resvalue) != 0)
+ {
+ op = &state->steps[op->d.rowcompare_step.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ROWCOMPARE_FINAL)
+ {
+ int32 cmpresult = DatumGetInt32(*op->resvalue);
+ RowCompareType rctype = op->d.rowcompare_final.rctype;
+
+ *op->resnull = false;
+ switch (rctype)
+ {
+ /* EQ and NE cases aren't allowed here */
+ case ROWCOMPARE_LT:
+ *op->resvalue = BoolGetDatum(cmpresult < 0);
+ break;
+ case ROWCOMPARE_LE:
+ *op->resvalue = BoolGetDatum(cmpresult <= 0);
+ break;
+ case ROWCOMPARE_GE:
+ *op->resvalue = BoolGetDatum(cmpresult >= 0);
+ break;
+ case ROWCOMPARE_GT:
+ *op->resvalue = BoolGetDatum(cmpresult > 0);
+ break;
+ default:
+ Assert(false);
+ break;
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_MINMAX)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalMinMax(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FIELDSELECT)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldSelect(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FIELDSTORE_DEFORM)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldStoreDeForm(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_FIELDSTORE_FORM)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldStoreForm(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Verify input for an ArrayRef reference (not assignment). If the
+ * array is null, return the entire result is NULL.
+ */
+ EEO_CASE(EEO_ARRAYREF_CHECKINPUT)
+ {
+ Assert(!op->d.arrayref.state->isassignment);
+
+ /*
+ * If refexpr yields NULL, and it's a fetch, then result is NULL.
+ */
+ if (*op->resnull &&
+ !op->d.arrayref.state->isassignment)
+ {
+ op = &state->steps[op->d.arrayref.jumpdone];
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Check whether a subscript is NULL and handle that.
+ */
+ EEO_CASE(EEO_ARRAYREF_CHECKSUBSCRIPT)
+ {
+ /* too complex for an inline implementation */
+ if (ExecEvalArrayRefCheckSubscript(state, op))
+ {
+ op++;
+ }
+ else
+ {
+ op = &state->steps[op->d.arrayref_checksubscript.jumpdone];
+ }
+
+ EEO_DISPATCH_DIRECT(op);
+ }
+
+ /*
+ * Fetch the old value in an arrayref, if referenced (via a
+ * CaseTestExpr) inside the assignment expression.
+ */
+ EEO_CASE(EEO_ARRAYREF_OLD)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefOld(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Perform ArrayRef assignment
+ */
+ EEO_CASE(EEO_ARRAYREF_ASSIGN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefAssign(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Fetch subset of an array.
+ */
+ EEO_CASE(EEO_ARRAYREF_FETCH)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefFetch(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_CONVERT_ROWTYPE)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConvertRowtype(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SCALARARRAYOP)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalScalarArrayOp(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_DOMAIN_NOTNULL)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConstraintNotNull(state, op);
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_DOMAIN_CHECK)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConstraintCheck(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_XMLEXPR)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalXmlExpr(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ /*
+ * Returns a Datum whose value is the value of the precomputed
+ * aggregate found in the given expression context.
+ */
+ EEO_CASE(EEO_AGGREF)
+ {
+ AggrefExprState *aggref = op->d.aggref.astate;
+
+ Assert(econtext->ecxt_aggvalues != NULL);
+
+ *op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+ *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_GROUPING_FUNC)
+ {
+ /* too complex/uncommon for an inline implementation */
+ ExecEvalGroupingFunc(state, op);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_WINDOW_FUNC)
+ {
+ WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+
+ Assert(econtext->ecxt_aggvalues != NULL);
+
+ *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+ *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_SUBPLAN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalSubPlan(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_ALTERNATIVE_SUBPLAN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalAlternativeSubPlan(state, op, econtext);
+
+ EEO_DISPATCH(op);
+ }
+
+ EEO_CASE(EEO_LAST)
+ {
+ /* unreachable */
+ Assert(false);
+ goto out;
+ }
+ }
+out:
+ *isnull = state->resnull;
+ return state->resvalue;
+}
+
+static Datum
+ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_outertuple;
+
+ return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_innertuple;
+
+ return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_scantuple;
+
+ return slot_getattr(slot, attnum, isnull);
+}
+
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+
+ *isnull = op->d.constval.isnull;
+ return op->d.constval.value;
+}
+
+static Datum
+ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ size_t resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_outertuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+static Datum
+ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ size_t resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_innertuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+static Datum
+ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ size_t resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_scantuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+static void
+ExecPrepareInterp(void)
+{
+ static bool prepared = false;
+
+ if (prepared)
+ return;
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+ if (dispatch_table == NULL)
+ dispatch_table = (void **) DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+
+ prepared = true;
+}
+
+ExprEvalOp
+ExecEvalStepOp(ExprState *state, ExprEvalStep *op)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+ if (state->flags & EEO_FLAG_JUMP_THREADED)
+ {
+ int i;
+
+ for (i = 0; i < EEO_LAST; i++)
+ {
+ if ((void *) op->opcode == dispatch_table[i])
+ {
+ return (ExprEvalOp) i;
+ }
+ }
+ elog(ERROR, "unknown opcode");
+ }
+#endif
+ return (ExprEvalOp) op->opcode;
+}
+
+/*
+ * Computes the value for a PARAM_EXEC parameter.
+ */
+void
+ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ ParamExecData *prm;
+
+ prm = &(econtext->ecxt_param_exec_vals[op->d.param.paramid]);
+ if (unlikely(prm->execPlan != NULL))
+ {
+ /*
+ * XXX: Worthwhile to extract this into a separate opcode?
+ */
+ ExecSetParamPlan(prm->execPlan, econtext);
+ /* ExecSetParamPlan should have processed this param... */
+ Assert(prm->execPlan == NULL);
+ }
+ *op->resvalue = prm->value;
+ *op->resnull = prm->isnull;
+}
+
+/*
+ * Computes the value for a PARAM_EXTERN parameter.
+ */
+void
+ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ ParamListInfo paramInfo = econtext->ecxt_param_list_info;
+ int paramId = op->d.param.paramid;
+
+ if (likely(paramInfo &&
+ paramId > 0 && paramId <= paramInfo->numParams))
+ {
+ ParamExternData *prm = ¶mInfo->params[paramId - 1];
+
+ /* give hook a chance in case parameter is dynamic */
+ /* XXX: separate opcode */
+ if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
+ (*paramInfo->paramFetch) (paramInfo, paramId);
+
+ if (likely(OidIsValid(prm->ptype)))
+ {
+ /* safety check in case hook did something unexpected */
+ /* XXX: assert instead? */
+ if (unlikely(prm->ptype != op->d.param.paramtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of parameter %d (%s) does not match that when preparing the plan (%s)",
+ paramId,
+ format_type_be(prm->ptype),
+ format_type_be(op->d.param.paramtype))));
+ *op->resvalue = prm->value;
+ *op->resnull = prm->isnull;
+ return;
+ }
+ }
+
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("no value found for parameter %d", paramId)));
+}
+
+/*
+ * Evaluate SQLValueFunction expressions.
+ */
+void
+ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op)
+{
+ FunctionCallInfoData fcinfo;
+ SQLValueFunction *svf = op->d.sqlvaluefunction.svf;
+
+ *op->resnull = 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:
+ *op->resvalue = DateADTGetDatum(GetSQLCurrentDate());
+ break;
+ case SVFOP_CURRENT_TIME:
+ case SVFOP_CURRENT_TIME_N:
+ *op->resvalue = TimeTzADTPGetDatum(GetSQLCurrentTime(svf->typmod));
+ break;
+ case SVFOP_CURRENT_TIMESTAMP:
+ case SVFOP_CURRENT_TIMESTAMP_N:
+ *op->resvalue = TimestampTzGetDatum(GetSQLCurrentTimestamp(svf->typmod));
+ break;
+ case SVFOP_LOCALTIME:
+ case SVFOP_LOCALTIME_N:
+ *op->resvalue = TimeADTGetDatum(GetSQLLocalTime(svf->typmod));
+ break;
+ case SVFOP_LOCALTIMESTAMP:
+ case SVFOP_LOCALTIMESTAMP_N:
+ *op->resvalue = TimestampGetDatum(GetSQLLocalTimestamp(svf->typmod));
+ break;
+ case SVFOP_CURRENT_ROLE:
+ case SVFOP_CURRENT_USER:
+ case SVFOP_USER:
+ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_user(&fcinfo);
+ *op->resnull = fcinfo.isnull;
+ break;
+ case SVFOP_SESSION_USER:
+ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = session_user(&fcinfo);
+ *op->resnull = fcinfo.isnull;
+ break;
+ case SVFOP_CURRENT_CATALOG:
+ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_database(&fcinfo);
+ *op->resnull = fcinfo.isnull;
+ break;
+ case SVFOP_CURRENT_SCHEMA:
+ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ *op->resvalue = current_schema(&fcinfo);
+ *op->resnull = fcinfo.isnull;
+ break;
+ }
+}
+
+void
+ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op)
+{
+ /*
+ * 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.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("WHERE CURRENT OF is not supported for this table type")));
+}
+
+/*
+ * Evaluate NullTest / IS NULL for rows.
+ */
+void
+ExecEvalRowNull(ExprState *state, ExprEvalStep *op)
+{
+ ExecEvalRowNullInt(state, op, true);
+}
+
+/*
+ * Evaluate NullTest / IS NOT NULL for rows.
+ */
+void
+ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op)
+{
+ ExecEvalRowNullInt(state, op, false);
+}
+
+static void
+ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op, bool checkisnull)
+{
+ Datum value = *op->resvalue;
+ bool isnull = *op->resnull;
+ HeapTupleHeader tuple;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
+ int att;
+
+ *op->resnull = false;
+
+ /* NULL row variables are treated just as NULL scalar columns */
+ if (isnull && checkisnull)
+ {
+ *op->resvalue = BoolGetDatum(true);
+ return;
+ }
+ else if (isnull)
+ {
+ *op->resvalue = BoolGetDatum(false);
+ return;
+ }
+
+ /*
+ * 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.
+ */
+
+ tuple = DatumGetHeapTupleHeader(value);
+
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+
+ /* Lookup tupdesc if first time through or if type changes */
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+
+ /*
+ * 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 (!checkisnull)
+ {
+ ReleaseTupleDesc(tupDesc);
+ *op->resvalue = BoolGetDatum(false);
+ return;
+ }
+ }
+ else
+ {
+ /* non-null field disproves IS NULL */
+ if (checkisnull)
+ {
+ ReleaseTupleDesc(tupDesc);
+ *op->resvalue = BoolGetDatum(false);
+ return;
+ }
+ }
+ }
+
+ ReleaseTupleDesc(tupDesc);
+ *op->resvalue = BoolGetDatum(true);
+}
+
+
+/*
+ * Evaluate ARRAY[] expressions.
+ */
+void
+ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op)
+{
+ ArrayType *result;
+ ArrayExpr *arrayExpr = op->d.arrayexpr.arrayexpr;
+ Oid element_type = arrayExpr->element_typeid;
+ int ndims = 0;
+ int dims[MAXDIM];
+ int lbs[MAXDIM];
+ int nelems = op->d.arrayexpr.nelems;
+
+ /* Set non-null as default */
+ *op->resnull = false;
+
+ if (!arrayExpr->multidims)
+ {
+ /* Elements are presumably of scalar type */
+ Datum *dvalues = op->d.arrayexpr.elemvalues;
+ bool *dnulls = op->d.arrayexpr.elemnulls;
+
+ ndims = 1;
+
+ /* Shouldn't happen here, but if length is 0, return empty array */
+ if (nelems == 0)
+ {
+ *op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+ return;
+ }
+
+ /* 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,
+ op->d.arrayexpr.elemlength,
+ op->d.arrayexpr.elembyval,
+ op->d.arrayexpr.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;
+ int32 dataoffset;
+ char *dat;
+ int iitem;
+ int elemoff;
+ int i;
+
+ subdata = (char **) palloc(nelems * sizeof(char *));
+ subbitmaps = (bits8 **) palloc(nelems * sizeof(bits8 *));
+ subbytes = (int *) palloc(nelems * sizeof(int));
+ subnitems = (int *) palloc(nelems * sizeof(int));
+
+ /* loop through and get data area from each element */
+ for (elemoff = 0; elemoff < nelems; elemoff++)
+ {
+ bool eisnull;
+ Datum arraydatum;
+ ArrayType *array;
+ int this_ndims;
+
+ arraydatum = op->d.arrayexpr.elemvalues[elemoff];
+ eisnull = op->d.arrayexpr.elemnulls[elemoff];
+
+ /* 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 */
+ {
+ *op->resvalue = PointerGetDatum(construct_empty_array(element_type));
+ return;
+ }
+ 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];
+ }
+ }
+
+ *op->resvalue = PointerGetDatum(result);
+}
+
+
+
+/*
+ * Evaluate an ArrayCoerceExpr expression.
+ */
+void
+ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op)
+{
+ ArrayCoerceExpr *acoerce = op->d.arraycoerce.coerceexpr;
+ Datum result;
+ FunctionCallInfoData locfcinfo;
+
+ /* input is in *step->resvalue/resnull */
+ result = *op->resvalue;
+ if (*op->resnull)
+ {
+ return; /* 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) = op->d.arraycoerce.resultelemtype;
+ *op->resvalue = PointerGetDatum(array);
+ return;
+ }
+
+ /* Initialize function cache if first time through */
+ if (op->d.arraycoerce.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(acoerce->elemfuncid, op->d.arraycoerce.elemfunc);
+ fmgr_info_set_expr((Node *) acoerce, op->d.arraycoerce.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, op->d.arraycoerce.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;
+
+ *op->resvalue = array_map(&locfcinfo, op->d.arraycoerce.resultelemtype,
+ op->d.arraycoerce.amstate);
+}
+
+/*
+ * Evaluate ROW() expressions.
+ *
+ * The individual columns have already been evaluated into
+ * op->d.row.elemvalues/nulls.
+ */
+void
+ExecEvalRow(ExprState *state, ExprEvalStep *op)
+{
+ HeapTuple tuple;
+
+ /* Set non-null as default */
+ *op->resnull = false;
+
+ /* build tuple from evaluated field values */
+ tuple = heap_form_tuple(op->d.row.tupdesc,
+ op->d.row.elemvalues,
+ op->d.row.elemnulls);
+
+ *op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+/*
+ * Evaluate GREATEST()/LEAST() type expression (note this is *not* MIN()/
+ * MAX()).
+ *
+ * All of the to-be-compared expressions have already been evaluated into
+ * ->op->d.minmax.values/nulls.
+ */
+void
+ExecEvalMinMax(ExprState *state, ExprEvalStep *op)
+{
+ int off;
+ Datum *values = op->d.minmax.values;
+ bool *nulls = op->d.minmax.nulls;
+ FunctionCallInfo fcinfo = op->d.minmax.fcinfo_data;
+ MinMaxOp operator = op->d.minmax.op;
+
+ /* set at initialization */
+ Assert(fcinfo->argnull[0] == false);
+ Assert(fcinfo->argnull[1] == false);
+
+ *op->resnull = true;
+
+ for (off = 0; off < op->d.minmax.nelems; off++)
+ {
+ /* ignore NULL inputs */
+ if (nulls[off])
+ continue;
+
+ if (*op->resnull)
+ {
+ /* first nonnull input, adopt value */
+ *op->resvalue = values[off];
+ *op->resnull = false;
+ }
+ else
+ {
+ int cmpresult;
+
+ /* apply comparison function */
+ fcinfo->isnull = false;
+ fcinfo->arg[0] = *op->resvalue;
+ fcinfo->arg[1] = values[off];
+ Assert(!fcinfo->isnull);
+
+ cmpresult = DatumGetInt32(FunctionCallInvoke(fcinfo));
+
+ if (cmpresult > 0 && operator == IS_LEAST)
+ *op->resvalue = values[off];
+ else if (cmpresult < 0 && operator == IS_GREATEST)
+ *op->resvalue = values[off];
+ }
+ }
+}
+
+/*
+ * Evaluate a FieldSelect node.
+ */
+void
+ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ AttrNumber fieldnum = op->d.fieldselect.fieldnum;
+ Datum tupDatum;
+ HeapTupleHeader tuple;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ Form_pg_attribute attr;
+ HeapTupleData tmptup;
+
+ tupDatum = *op->resvalue;
+
+ /* this test covers the isDone exception too: */
+ if (*op->resnull)
+ return;
+
+ 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,
+ &op->d.fieldselect.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)
+ {
+ *op->resnull = true;
+ return;
+ }
+
+ /* Check for type mismatch --- possible after ALTER COLUMN TYPE? */
+ /* As in ExecEvalScalarVar, we should but can't check typmod */
+ if (op->d.fieldselect.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(op->d.fieldselect.resulttype))));
+
+ /* heap_getattr needs a HeapTuple not a bare HeapTupleHeader */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ tmptup.t_data = tuple;
+
+ *op->resvalue = heap_getattr(&tmptup,
+ fieldnum,
+ tupDesc,
+ op->resnull);
+}
+
+/*
+ * Deform tuple before evaluating the individual new values as part of a
+ * FieldStore expression.
+ */
+void
+ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ /* Lookup tupdesc if first time through or after rescan */
+ get_cached_rowtype(op->d.fieldstore.fstore->resulttype, -1,
+ op->d.fieldstore.argdesc, econtext);
+
+ if (*op->resnull)
+ {
+ /* Convert null input tuple into an all-nulls row */
+ memset(op->d.fieldstore.nulls, true,
+ MaxTupleAttributeNumber * sizeof(bool));
+ }
+ else
+ {
+ /*
+ * heap_deform_tuple needs a HeapTuple not a bare HeapTupleHeader. We
+ * set all the fields in the struct just in case.
+ */
+ Datum tupDatum = *op->resvalue;
+ 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, *op->d.fieldstore.argdesc,
+ op->d.fieldstore.values,
+ op->d.fieldstore.nulls);
+ }
+}
+
+/*
+ * Compute the new wholerow datum after each individual row part of a
+ * FieldStore expression has been evaluated.
+ */
+void
+ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ HeapTuple tuple;
+
+ /* Result is never null */
+ *op->resnull = false;
+
+ tuple = heap_form_tuple(*op->d.fieldstore.argdesc,
+ op->d.fieldstore.values,
+ op->d.fieldstore.nulls);
+
+ *op->resvalue = HeapTupleGetDatum(tuple);
+}
+
+void
+ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op)
+{
+ ArrayRefState *arefstate = op->d.arrayref.state;
+
+ if (*op->resnull)
+ {
+ arefstate->prevnull = true;
+ arefstate->prevvalue = (Datum) 0;
+ }
+ else if (arefstate->numlower == 0)
+ {
+ arefstate->prevvalue =
+ array_get_element(*op->resvalue,
+ arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign,
+ &arefstate->prevnull);
+ }
+ else
+ {
+ /* this is currently unreachable */
+ arefstate->prevvalue =
+ array_get_slice(*op->resvalue,
+ arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->lowerindex,
+ arefstate->upperprovided,
+ arefstate->lowerprovided,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign);
+ }
+}
+
+void
+ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op)
+{
+ ArrayRefState *arefstate = op->d.arrayref.state;
+
+ /*
+ * 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 (arefstate->refattrlength > 0 &&
+ (*op->resnull || arefstate->replacenull))
+ {
+ return;
+ }
+
+ /*
+ * 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 (*op->resnull)
+ {
+ *op->resvalue = PointerGetDatum(construct_empty_array(arefstate->refelemtype));
+ *op->resnull = false;
+ }
+
+ if (arefstate->numlower == 0)
+ {
+ *op->resvalue =
+ array_set_element(*op->resvalue, arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->replacevalue,
+ arefstate->replacenull,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign);
+ }
+ else
+ {
+ *op->resvalue =
+ array_set_slice(*op->resvalue, arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->lowerindex,
+ arefstate->upperprovided,
+ arefstate->lowerprovided,
+ arefstate->replacevalue,
+ arefstate->replacenull,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign);
+ }
+}
+
+void
+ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op)
+{
+ ArrayRefState *arefstate = op->d.arrayref.state;
+
+ if (arefstate->numlower == 0)
+ {
+
+ *op->resvalue =
+ array_get_element(*op->resvalue,
+ arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign,
+ op->resnull);
+ }
+ else
+ {
+ *op->resvalue =
+ array_get_slice(*op->resvalue,
+ arefstate->numupper,
+ arefstate->upperindex,
+ arefstate->lowerindex,
+ arefstate->upperprovided,
+ arefstate->lowerprovided,
+ arefstate->refattrlength,
+ arefstate->refelemlength,
+ arefstate->refelembyval,
+ arefstate->refelemalign);
+ }
+}
+
+bool
+ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op)
+{
+ ArrayRefState *arefstate = op->d.arrayref_checksubscript.state;
+ int off = op->d.arrayref_checksubscript.off;
+ bool *nulls;
+ Datum *values;
+ int *indexes;
+
+ if (op->d.arrayref_checksubscript.isupper)
+ {
+ nulls = arefstate->uppernull;
+ values = arefstate->upper;
+ indexes = arefstate->upperindex;
+ }
+ else
+ {
+ nulls = arefstate->lowernull;
+ values = arefstate->lower;
+ indexes = arefstate->lowerindex;
+ }
+
+ /* If any index expr yields NULL, result is NULL or error */
+ if (nulls[off])
+ {
+ if (arefstate->isassignment)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("array subscript in assignment must not be null")));
+ *op->resnull = true;
+ return false;
+ }
+
+ /* convert datum to int */
+ indexes[off] = values[off];
+
+ return true;
+}
+
+/*
+ * Evaluate a rowtype coercion operation. This may require rearranging field
+ * positions.
+ */
+void
+ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ ConvertRowtypeExpr *convert = op->d.convert_rowtype.convert;
+ HeapTuple result;
+ Datum tupDatum;
+ HeapTupleHeader tuple;
+ HeapTupleData tmptup;
+ TupleDesc indesc,
+ outdesc;
+
+ tupDatum = *op->resvalue;
+
+ /* this test covers the isDone exception too: */
+ if (*op->resnull)
+ return;
+
+ tuple = DatumGetHeapTupleHeader(tupDatum);
+
+ /* Lookup tupdescs if first time through or after rescan */
+ if (op->d.convert_rowtype.indesc == NULL)
+ {
+ get_cached_rowtype(exprType((Node *) convert->arg), -1,
+ &op->d.convert_rowtype.indesc,
+ econtext);
+ op->d.convert_rowtype.initialized = false;
+ }
+ if (op->d.convert_rowtype.outdesc == NULL)
+ {
+ get_cached_rowtype(convert->resulttype, -1,
+ &op->d.convert_rowtype.outdesc,
+ econtext);
+ op->d.convert_rowtype.initialized = false;
+ }
+
+ indesc = op->d.convert_rowtype.indesc;
+ outdesc = op->d.convert_rowtype.outdesc;
+
+ /*
+ * 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) == indesc->tdtypeid ||
+ HeapTupleHeaderGetTypeId(tuple) == RECORDOID);
+
+ /* if first time through, initialize conversion map */
+ if (!op->d.convert_rowtype.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 */
+ op->d.convert_rowtype.map =
+ convert_tuples_by_name(indesc, outdesc,
+ gettext_noop("could not convert row type"));
+ op->d.convert_rowtype.initialized = true;
+
+ MemoryContextSwitchTo(old_cxt);
+ }
+
+ /*
+ * No-op if no conversion needed (not clear this can happen here).
+ */
+ if (op->d.convert_rowtype.map == NULL)
+ return;
+
+ /*
+ * do_convert_tuple needs a HeapTuple not a bare HeapTupleHeader.
+ */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
+ tmptup.t_data = tuple;
+
+ result = do_convert_tuple(&tmptup, op->d.convert_rowtype.map);
+
+ *op->resvalue = HeapTupleGetDatum(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.
+ */
+void
+ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op)
+{
+ ScalarArrayOpExpr *opexpr = op->d.scalararrayop.opexpr;
+ bool useOr = opexpr->useOr;
+ FunctionCallInfo fcinfo = op->d.scalararrayop.fcinfo_data;
+ bool strictfunc = op->d.scalararrayop.finfo->fn_strict;
+ ArrayType *arr;
+ int nitems;
+ Datum result;
+ bool resultnull;
+ int i;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ char *s;
+ bits8 *bitmap;
+ int bitmask;
+
+ /*
+ * 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 (*op->resnull)
+ {
+ return;
+ }
+
+ /* no "bad" nulls, okay to fetch and detoast the array */
+ arr = DatumGetArrayTypeP(*op->resvalue);
+
+ /*
+ * 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)
+ {
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(!useOr);
+ return;
+ }
+
+ /*
+ * If the scalar is NULL, and the function is strict, return NULL; no
+ * point in iterating the loop.
+ */
+ if (fcinfo->argnull[0] && strictfunc)
+ {
+ *op->resnull = true;
+ return;
+ }
+
+ /*
+ * 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 (op->d.scalararrayop.element_type != ARR_ELEMTYPE(arr))
+ {
+ get_typlenbyvalalign(ARR_ELEMTYPE(arr),
+ &op->d.scalararrayop.typlen,
+ &op->d.scalararrayop.typbyval,
+ &op->d.scalararrayop.typalign);
+ op->d.scalararrayop.element_type = ARR_ELEMTYPE(arr);
+ }
+
+ typlen = op->d.scalararrayop.typlen;
+ typbyval = op->d.scalararrayop.typbyval;
+ typalign = op->d.scalararrayop.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] && strictfunc)
+ {
+ fcinfo->isnull = true;
+ thisresult = (Datum) 0;
+ }
+ else
+ {
+ fcinfo->isnull = false;
+ thisresult = (op->d.scalararrayop.fn_addr) (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;
+ }
+ }
+ }
+
+ *op->resnull = resultnull;
+ *op->resvalue = result;
+}
+
+/*
+ * Evaluate a NOT NULL constraint.
+ */
+void
+ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op)
+{
+ if (*op->resnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NOT_NULL_VIOLATION),
+ errmsg("domain %s does not allow null values",
+ format_type_be(op->d.domaincheck.resulttype)),
+ errdatatype(op->d.domaincheck.resulttype)));
+}
+
+/*
+ * Evaluate a CHECK constraint.
+ */
+void
+ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op)
+{
+ if (!*op->d.domaincheck.checknull &&
+ !DatumGetBool(*op->d.domaincheck.checkvalue))
+ ereport(ERROR,
+ (errcode(ERRCODE_CHECK_VIOLATION),
+ errmsg("value for domain %s violates check constraint \"%s\"",
+ format_type_be(op->d.domaincheck.resulttype),
+ op->d.domaincheck.constraintname),
+ errdomainconstraint(op->d.domaincheck.resulttype,
+ op->d.domaincheck.constraintname)));
+}
+
+/*
+ * Evaluate the various forms of XmlExpr.
+ */
+void
+ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op)
+{
+ XmlExpr *xexpr = op->d.xmlexpr.xexpr;
+ Datum value;
+ ListCell *arg;
+ ListCell *narg;
+ int i;
+
+ *op->resnull = true; /* until we get a result */
+ *op->resvalue = (Datum) 0;
+
+ switch (xexpr->op)
+ {
+ case IS_XMLCONCAT:
+ {
+ Datum *argvalue = op->d.xmlexpr.argvalue;
+ bool *argnull = op->d.xmlexpr.argnull;
+ List *values = NIL;
+
+ for (i = 0; i < list_length(xexpr->args); i++)
+ {
+ if (!argnull[i])
+ values = lappend(values, DatumGetPointer(argvalue[i]));
+ }
+
+ if (list_length(values) > 0)
+ {
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(xmlconcat(values));
+ }
+ }
+ break;
+
+ case IS_XMLFOREST:
+ {
+ Datum *argvalue = op->d.xmlexpr.named_argvalue;
+ bool *argnull = op->d.xmlexpr.named_argnull;
+ StringInfoData buf;
+
+ initStringInfo(&buf);
+
+ i = 0;
+ forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
+ {
+ char *argname = strVal(lfirst(narg));
+ Expr *e = (Expr *) lfirst(arg);
+
+ if (!argnull[i])
+ {
+ value = argvalue[i];
+ appendStringInfo(&buf, "<%s>%s</%s>",
+ argname,
+ map_sql_value_to_xml_value(value, exprType((Node *) e), true),
+ argname);
+ *op->resnull = false;
+ }
+ i++;
+ }
+
+ if (*op->resnull)
+ {
+ pfree(buf.data);
+ }
+ else
+ {
+ text *result;
+
+ result = cstring_to_text_with_len(buf.data, buf.len);
+ pfree(buf.data);
+
+ *op->resvalue = PointerGetDatum(result);
+ }
+ }
+ break;
+
+ case IS_XMLELEMENT:
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(
+ xmlelement(xexpr,
+ op->d.xmlexpr.named_argnull,
+ op->d.xmlexpr.named_argvalue,
+ op->d.xmlexpr.argnull,
+ op->d.xmlexpr.argvalue));
+ break;
+ case IS_XMLPARSE:
+ {
+ Datum *argvalue = op->d.xmlexpr.argvalue;
+ bool *argnull = op->d.xmlexpr.argnull;
+ text *data;
+ bool preserve_whitespace;
+
+ /* arguments are known to be text, bool */
+ Assert(list_length(xexpr->args) == 2);
+
+ if (argnull[0])
+ return;
+ value = argvalue[0];
+ data = DatumGetTextPP(value);
+
+ if (argnull[1]) /* probably can't happen */
+ return;
+ value = argvalue[1];
+ preserve_whitespace = DatumGetBool(value);
+
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(
+ xmlparse(data, xexpr->xmloption, preserve_whitespace));
+ }
+ break;
+
+ case IS_XMLPI:
+ {
+ text *arg;
+ bool isnull;
+
+ /* optional argument is known to be text */
+ Assert(list_length(xexpr->args) <= 1);
+
+ if (xexpr->args)
+ {
+ isnull = op->d.xmlexpr.argnull[0];
+ if (isnull)
+ arg = NULL;
+ else
+ arg = DatumGetTextPP(op->d.xmlexpr.argvalue[0]);
+
+ }
+ else
+ {
+ arg = NULL;
+ isnull = false;
+ }
+
+ *op->resvalue = PointerGetDatum(xmlpi(xexpr->name, arg,
+ isnull, op->resnull));
+ }
+ break;
+
+ case IS_XMLROOT:
+ {
+ Datum *argvalue = op->d.xmlexpr.argvalue;
+ bool *argnull = op->d.xmlexpr.argnull;
+ xmltype *data;
+ text *version;
+ int standalone;
+
+ /* arguments are known to be xml, text, int */
+ Assert(list_length(xexpr->args) == 3);
+
+ if (argnull[0])
+ return;
+ data = DatumGetXmlP(argvalue[0]);
+
+ if (argnull[1])
+ version = NULL;
+ else
+ version = DatumGetTextPP(argvalue[1]);
+
+ Assert(!argnull[2]); /* always present */
+ standalone = DatumGetInt32(argvalue[2]);
+
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(xmlroot(data,
+ version,
+ standalone));
+ }
+ break;
+
+ case IS_XMLSERIALIZE:
+ {
+ Datum *argvalue = op->d.xmlexpr.argvalue;
+ bool *argnull = op->d.xmlexpr.argnull;
+
+ /* argument type is known to be xml */
+ Assert(list_length(xexpr->args) == 1);
+
+ if (argnull[0])
+ return;
+
+ value = argvalue[0];
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(
+ xmltotext_with_xmloption(DatumGetXmlP(value),
+ xexpr->xmloption));
+ }
+ break;
+
+ case IS_DOCUMENT:
+ {
+ Datum *argvalue = op->d.xmlexpr.argvalue;
+ bool *argnull = op->d.xmlexpr.argnull;
+
+ /* optional argument is known to be xml */
+ Assert(list_length(xexpr->args) == 1);
+
+ if (argnull[0])
+ break;
+
+ value = argvalue[0];
+ *op->resnull = false;
+ *op->resvalue =
+ BoolGetDatum(xml_is_document(DatumGetXmlP(value)));
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized XML operation");
+ }
+}
+
+/*
+ * ExecEvalGroupingFunc
+ *
+ * Computes 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.
+ */
+void
+ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op)
+{
+ int result = 0;
+ int attnum = 0;
+ Bitmapset *grouped_cols = op->d.grouping_func.parent->grouped_cols;
+ ListCell *lc;
+
+ *op->resnull = false;
+
+ foreach(lc, op->d.grouping_func.clauses)
+ {
+ attnum = lfirst_int(lc);
+
+ result = result << 1;
+
+ if (!bms_is_member(attnum, grouped_cols))
+ result = result | 1;
+ }
+
+ *op->resvalue = Int32GetDatum(result);
+}
+
+/*
+ * Hand off evaluation of a subplan to nodeSubplan.c
+ */
+void
+ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ SubPlanState *sstate = op->d.subplan.sstate;
+
+ /* could potentially be nested, so make sure there's enough stack */
+ check_stack_depth();
+
+ *op->resvalue = ExecSubPlan(sstate, econtext, op->resnull);
+}
+
+/*
+ * Hand off evaluation of an alternative subplan to nodeSubplan.c
+ */
+void
+ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ AlternativeSubPlanState *asstate = op->d.alternative_subplan.asstate;
+
+ /* could potentially be nested, so make sure there's enough stack */
+ check_stack_depth();
+
+ *op->resvalue = ExecAlternativeSubPlan(asstate, econtext, op->resnull);
+}
+
+/*
+ * Evaluate a wholerow Var expression.
+ *
+ * Returns a Datum whose value is the value of a whole-row range variable with
+ * respect to given expression context.
+ */
+void
+ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext)
+{
+ Var *variable = op->d.wholerow.var;
+ TupleTableSlot *slot;
+ TupleDesc output_tupdesc;
+ MemoryContext oldcontext;
+ bool needslow = false;
+ HeapTupleHeader dtuple;
+ HeapTuple tuple;
+
+ /* 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;
+ }
+
+
+ /* Apply the junkfilter if any */
+ if (op->d.wholerow.junkFilter != NULL)
+ slot = ExecFilterJunk(op->d.wholerow.junkFilter, slot);
+
+ /*
+ * First time through, perform initialization.
+ *
+ * XXX: It'd be great if this could be moved to the the expression
+ * initialization phase, but due to using slots that's currently not
+ * feasible.
+ */
+ if (op->d.wholerow.first)
+ {
+ /*
+ * 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 */
+ op->d.wholerow.tupdesc = BlessTupleDesc(output_tupdesc);
+
+ op->d.wholerow.first = false;
+ }
+
+ if (needslow)
+ {
+ TupleDesc tupleDesc = slot->tts_tupleDescriptor;
+ TupleDesc var_tupdesc = op->d.wholerow.tupdesc;
+ int i;
+
+ tuple = ExecFetchSlotTuple(slot);
+
+
+ /* 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, op->d.wholerow.tupdesc->tdtypeid);
+ HeapTupleHeaderSetTypMod(dtuple, op->d.wholerow.tupdesc->tdtypmod);
+
+ *op->resnull = false;
+ *op->resvalue = PointerGetDatum(dtuple);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+}
resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(n * sizeof(FmgrInfo));
- resultRelInfo->ri_TrigWhenExprs = (List **)
+ resultRelInfo->ri_TrigWhenExprs = (ExprState **)
palloc0(n * sizeof(List *));
if (instrument_options)
resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
ConstrCheck *check = rel->rd_att->constr->check;
ExprContext *econtext;
MemoryContext oldContext;
- List *qual;
int i;
/*
{
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
resultRelInfo->ri_ConstraintExprs =
- (List **) palloc(ncheck * sizeof(List *));
+ (ExprState **) palloc(ncheck * sizeof(ExprState *));
for (i = 0; i < ncheck; i++)
{
- /* ExecQual wants implicit-AND form */
+ List *qual;
+
+ /* ExecPrepareQual wants implicit-AND form */
qual = make_ands_implicit(stringToNode(check[i].ccbin));
- resultRelInfo->ri_ConstraintExprs[i] = (List *)
- ExecPrepareExpr((Expr *) qual, estate);
+ resultRelInfo->ri_ConstraintExprs[i] =
+ ExecPrepareCheck(qual, estate);
}
MemoryContextSwitchTo(oldContext);
}
/* And evaluate the constraints */
for (i = 0; i < ncheck; i++)
{
- qual = resultRelInfo->ri_ConstraintExprs[i];
+ ExprState *qual = resultRelInfo->ri_ConstraintExprs[i];
/*
* NOTE: SQL specifies that a NULL result from a constraint expression
- * is not to be treated as a failure. Therefore, tell ExecQual to
- * return TRUE for NULL.
+ * is not to be treated as a failure. Therefore, use ExecCheck not
+ * ExecQual.
*/
- if (!ExecQual(qual, econtext, true))
+ if (!ExecCheck(qual, econtext))
return check[i].ccname;
}
{
List *qual = resultRelInfo->ri_PartitionCheck;
- resultRelInfo->ri_PartitionCheckExpr = (List *)
- ExecPrepareExpr((Expr *) qual, estate);
+ resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
}
/*
* As in case of the catalogued constraints, we treat a NULL result as
* success here, not a failure.
*/
- return ExecQual(resultRelInfo->ri_PartitionCheckExpr, econtext, true);
+ return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
}
/*
* is visible (in the case of a view) or that it passes the
* 'with-check' policy (in the case of row security). If the qual
* evaluates to NULL or FALSE, then the new tuple won't be included in
- * the view or doesn't pass the 'with-check' policy for the table. We
- * need ExecQual to return FALSE for NULL to handle the view case (the
- * opposite of what we do above for CHECK constraints).
+ * the view or doesn't pass the 'with-check' policy for the table.
*/
- if (!ExecQual((List *) wcoExpr, econtext, false))
+ if (!ExecQual(wcoExpr, econtext))
{
char *val_desc;
Bitmapset *modifiedCols;
/*-------------------------------------------------------------------------
*
* execQual.c
- * Routines to evaluate qualification and targetlist expressions
+ * Former 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
*
*-------------------------------------------------------------------------
*/
-/*
- * 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"
/* 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 init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+ MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
+static void ShutdownSetExpr(Datum arg);
static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
-static void ExecPrepareTuplestoreResult(FuncExprState *fcache,
+static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
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).
+/*
+ * GetAttributeByName
+ * GetAttributeByNum
*
- * 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.
- *----------
+ * 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.
*/
-static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull)
+Datum
+GetAttributeByNum(HeapTupleHeader tuple,
+ AttrNumber attrno,
+ 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;
+ Datum result;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull);
+ if (!AttributeNumberIsValid(attrno))
+ elog(ERROR, "invalid attribute number %d", attrno);
- /*
- * 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;
- }
+ if (isNull == NULL)
+ elog(ERROR, "a NULL isNull pointer was passed");
- foreach(l, astate->refupperindexpr)
+ if (tuple == NULL)
{
- 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;
- }
+ /* Kinda bogus but compatible with old behavior... */
+ *isNull = true;
+ return (Datum) 0;
}
- 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;
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
+ /*
+ * 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;
- /*
- * 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;
+ result = heap_getattr(&tmptup,
+ attrno,
+ tupDesc,
+ 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;
- }
+ ReleaseTupleDesc(tupDesc);
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull);
+ return result;
+}
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
+Datum
+GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
+{
+ AttrNumber attrno;
+ Datum result;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupDesc;
+ HeapTupleData tmptup;
+ int i;
- /*
- * 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;
+ if (attname == NULL)
+ elog(ERROR, "invalid attribute name");
- /*
- * 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 (isNull == NULL)
+ elog(ERROR, "a NULL isNull pointer was passed");
- 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 (tuple == NULL)
+ {
+ /* Kinda bogus but compatible with old behavior... */
+ *isNull = true;
+ return (Datum) 0;
}
- 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);
-}
+ tupType = HeapTupleHeaderGetTypeId(tuple);
+ tupTypmod = HeapTupleHeaderGetTypMod(tuple);
+ tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
-/*
- * 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))
+ attrno = InvalidAttrNumber;
+ for (i = 0; i < tupDesc->natts; i++)
{
- FieldStore *fstore = (FieldStore *) exprstate->expr;
-
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
+ if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
+ {
+ attrno = tupDesc->attrs[i]->attnum;
+ break;
+ }
}
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
-}
+ if (attrno == InvalidAttrNumber)
+ elog(ERROR, "attribute \"%s\" does not exist", attname);
-/* ----------------------------------------------------------------
- * 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");
+ /*
+ * 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;
- *isNull = econtext->ecxt_aggnulls[aggref->aggno];
- return econtext->ecxt_aggvalues[aggref->aggno];
-}
+ result = heap_getattr(&tmptup,
+ attrno,
+ tupDesc,
+ isNull);
-/* ----------------------------------------------------------------
- * 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");
+ ReleaseTupleDesc(tupDesc);
- *isNull = econtext->ecxt_aggnulls[wfunc->wfuncno];
- return econtext->ecxt_aggvalues[wfunc->wfuncno];
+ return result;
}
-/* ----------------------------------------------------------------
- * 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.
- * ----------------------------------------------------------------
+/*
+ * init_sexpr - initialize a SetExprState node during first use
*/
-static Datum
-ExecEvalScalarVar(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
+static void
+init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
+ MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
{
- Var *variable = (Var *) exprstate->expr;
- TupleTableSlot *slot;
- AttrNumber attnum;
+ AclResult aclresult;
- /* 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;
+ /* 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);
- case OUTER_VAR: /* get the tuple from the outer node */
- slot = econtext->ecxt_outertuple;
- break;
+ /*
+ * 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(sexpr->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)));
- /* INDEX_VAR is handled by default case */
+ /* Set up the primary fmgr lookup information */
+ fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
+ fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
+ /* Initialize the function call parameter struct as well */
+ InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
+ list_length(sexpr->args),
+ input_collation, NULL, NULL);
- attnum = variable->varattno;
+ /* If function returns set, check if that's allowed by caller */
+ if (sexpr->func.fn_retset && !allowSRF)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
- /* This was checked by ExecInitExpr */
- Assert(attnum != InvalidAttrNumber);
+ /* Otherwise, ExecInitExpr should have marked the sexpr correctly */
+ Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
- /*
- * 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)
+ /* If function returns set, prepare expected tuple descriptor */
+ if (sexpr->func.fn_retset && needDescForSRF)
{
- TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
- Form_pg_attribute attr;
+ TypeFuncClass functypclass;
+ Oid funcrettype;
+ TupleDesc tupdesc;
+ MemoryContext oldcontext;
- if (attnum > slot_tupdesc->natts) /* should never happen */
- elog(ERROR, "attribute number %d exceeds number of columns %d",
- attnum, slot_tupdesc->natts);
+ functypclass = get_expr_result_type(sexpr->func.fn_expr,
+ &funcrettype,
+ &tupdesc);
- attr = slot_tupdesc->attrs[attnum - 1];
+ /* Must save tupdesc in sexpr's context */
+ oldcontext = MemoryContextSwitchTo(sexprCxt);
- /* can't check type if dropped, since atttypid is probably 0 */
- if (!attr->attisdropped)
+ if (functypclass == TYPEFUNC_COMPOSITE)
{
- 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))));
+ /* Composite data type, e.g. a table's row type */
+ Assert(tupdesc);
+ /* Must copy it out of typcache for safety */
+ sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
+ sexpr->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);
+ sexpr->funcResultDesc = tupdesc;
+ sexpr->funcReturnsTuple = false;
+ }
+ else if (functypclass == TYPEFUNC_RECORD)
+ {
+ /* This will work if function doesn't need an expectedDesc */
+ sexpr->funcResultDesc = NULL;
+ sexpr->funcReturnsTuple = true;
+ }
+ else
+ {
+ /* Else, we will fail if function needs an expectedDesc */
+ sexpr->funcResultDesc = NULL;
}
- }
- /* Skip the checking on future executions of node */
- exprstate->evalfunc = ExecEvalScalarVarFast;
+ MemoryContextSwitchTo(oldcontext);
+ }
+ else
+ sexpr->funcResultDesc = NULL;
- /* Fetch the value from the slot */
- return slot_getattr(slot, attnum, isNull);
+ /* Initialize additional state */
+ sexpr->funcResultStore = NULL;
+ sexpr->funcResultSlot = NULL;
+ sexpr->shutdown_reg = false;
}
-/* ----------------------------------------------------------------
- * ExecEvalScalarVarFast
- *
- * Returns a Datum for a scalar variable.
- * ----------------------------------------------------------------
+/*
+ * callback function in case a SetExpr needs to be shut down before it has
+ * been run to completion
*/
-static Datum
-ExecEvalScalarVarFast(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
+static void
+ShutdownSetExpr(Datum arg)
{
- Var *variable = (Var *) exprstate->expr;
- TupleTableSlot *slot;
- AttrNumber attnum;
+ SetExprState *sexpr = (SetExprState *) DatumGetPointer(arg);
- /* 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 */
+ /* If we have a slot, make sure it's let go of any tuplestore pointer */
+ if (sexpr->funcResultSlot)
+ ExecClearTuple(sexpr->funcResultSlot);
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
- }
+ /* Release any open tuplestore */
+ if (sexpr->funcResultStore)
+ tuplestore_end(sexpr->funcResultStore);
+ sexpr->funcResultStore = NULL;
- attnum = variable->varattno;
+ /* Clear any active set-argument state */
+ sexpr->setArgsValid = false;
- /* Fetch the value from the slot */
- return slot_getattr(slot, attnum, isNull);
+ /* execUtils will deregister the callback... */
+ sexpr->shutdown_reg = false;
}
-/* ----------------------------------------------------------------
- * 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.
- * ----------------------------------------------------------------
+/*
+ * Evaluate arguments for a function.
*/
-static Datum
-ExecEvalWholeRowVar(WholeRowVarExprState *wrvstate, ExprContext *econtext,
- bool *isNull)
+static void
+ExecEvalFuncArgs(FunctionCallInfo fcinfo,
+ List *argList,
+ ExprContext *econtext)
{
- 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);
+ int i;
+ ListCell *arg;
- /* Get the input slot we want */
- switch (variable->varno)
+ i = 0;
+ foreach(arg, argList)
{
- 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 */
+ ExprState *argstate = (ExprState *) lfirst(arg);
- default: /* get the tuple from the relation being
- * scanned */
- slot = econtext->ecxt_scantuple;
- break;
+ fcinfo->arg[i] = ExecEvalExpr(argstate,
+ econtext,
+ &fcinfo->argnull[i]);
+ i++;
}
- /*
- * 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);
- }
- }
- }
+ Assert(i == fcinfo->nargs);
+}
- /* Apply the junkfilter if any */
- if (wrvstate->wrv_junkFilter != NULL)
- slot = ExecFilterJunk(wrvstate->wrv_junkFilter, slot);
+/*
+ * 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(SetExprState *sexpr,
+ ExprContext *econtext,
+ Tuplestorestate *resultStore,
+ TupleDesc resultDesc)
+{
+ sexpr->funcResultStore = resultStore;
- /*
- * If the Var identifies a named composite type, we must check that the
- * actual tuple type is compatible with it.
- */
- if (variable->vartype != RECORDOID)
+ if (sexpr->funcResultSlot == NULL)
{
- TupleDesc var_tupdesc;
- TupleDesc slot_tupdesc;
- int i;
+ /* Create a slot so we can read data out of the tuplestore */
+ TupleDesc slotDesc;
+ MemoryContext oldcontext;
+
+ oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
/*
- * 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.
+ * If we were not able to determine the result rowtype from context,
+ * and the function didn't return a tupdesc, we have to fail.
*/
- 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++)
+ if (sexpr->funcResultDesc)
+ slotDesc = sexpr->funcResultDesc;
+ else if (resultDesc)
{
- 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 */
+ /* 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 */
}
- /*
- * 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);
+ sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
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 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 (econtext->ecxt_estate &&
- variable->varno <= list_length(econtext->ecxt_estate->es_range_table))
+ if (resultDesc)
{
- RangeTblEntry *rte = rt_fetch(variable->varno,
- econtext->ecxt_estate->es_range_table);
+ if (sexpr->funcResultDesc)
+ tupledesc_match(sexpr->funcResultDesc, resultDesc);
- if (rte->eref)
- ExecTypeSetColNames(output_tupdesc, rte->eref->colnames);
+ /*
+ * 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);
}
- /* 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)
+ /* Register cleanup callback if we didn't already */
+ if (!sexpr->shutdown_reg)
{
- 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;
+ RegisterExprContextCallback(econtext,
+ ShutdownSetExpr,
+ PointerGetDatum(sexpr));
+ sexpr->shutdown_reg = true;
}
-
- /* 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
+/*
+ * 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.
*
- * Returns a Datum for a whole-row variable, in the "slow" case where
- * we can't just copy the subplan's output.
- * ----------------------------------------------------------------
+ * 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 Datum
-ExecEvalWholeRowSlow(WholeRowVarExprState *wrvstate, ExprContext *econtext,
- bool *isNull)
+static void
+tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
{
- Var *variable = (Var *) wrvstate->xprstate.expr;
- TupleTableSlot *slot;
- HeapTuple tuple;
- TupleDesc tupleDesc;
- TupleDesc var_tupdesc;
- HeapTupleHeader dtuple;
int i;
- *isNull = false;
+ 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)));
- /* Get the input slot we want */
- switch (variable->varno)
+ for (i = 0; i < dst_tupdesc->natts; i++)
{
- 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;
+ Form_pg_attribute dattr = dst_tupdesc->attrs[i];
+ Form_pg_attribute sattr = src_tupdesc->attrs[i];
- /* 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 (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 (!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)
+ if (dattr->attlen != sattr->attlen ||
+ dattr->attalign != sattr->attalign)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("table row type and query-specified row type do not match"),
+ 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)));
}
-
- /*
- * 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.
+/*
+ * ExecMakeFunctionResultSet
*
- * 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.
- * ----------------------------------------------------------------
+ * 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).
*/
-static Datum
-ExecEvalConst(ExprState *exprstate, ExprContext *econtext,
- bool *isNull)
+Datum
+ExecMakeFunctionResultSet(SetExprState *fcache,
+ ExprContext *econtext,
+ bool *isNull,
+ ExprDoneCond *isDone)
{
- Const *con = (Const *) exprstate->expr;
+ List *arguments;
+ Datum result;
+ FunctionCallInfo fcinfo;
+ PgStat_FunctionCallUsage fcusage;
+ ReturnSetInfo rsinfo;
+ bool callit;
+ int i;
- *isNull = con->constisnull;
- return con->constvalue;
-}
+restart:
-/* ----------------------------------------------------------------
- * 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;
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
/*
- * PARAM_EXEC params (internal executor parameters) are stored in the
- * ecxt_param_exec_vals array, and can be accessed by array index.
+ * 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.
*/
- prm = &(econtext->ecxt_param_exec_vals[thisParamId]);
- if (prm->execPlan != NULL)
+ if (fcache->funcResultStore)
{
- /* Parameter not evaluated yet, so go do it */
- ExecSetParamPlan(prm->execPlan, econtext);
- /* ExecSetParamPlan should have processed this param... */
- Assert(prm->execPlan == NULL);
+ 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;
}
- *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.
+ * arguments is a list of expressions to evaluate before passing to the
+ * function manager. We skip the evaluation if it was already done in the
+ * previous call (ie, we are continuing the evaluation of a set-valued
+ * function). Otherwise, collect the current argument values into fcinfo.
*/
- if (paramInfo &&
- thisParamId > 0 && thisParamId <= paramInfo->numParams)
+ fcinfo = &fcache->fcinfo_data;
+ arguments = fcache->args;
+ if (!fcache->setArgsValid)
+ ExecEvalFuncArgs(fcinfo, arguments, econtext);
+ else
{
- ParamExternData *prm = ¶mInfo->params[thisParamId - 1];
+ /* Reset flag (we may set it again below) */
+ fcache->setArgsValid = false;
+ }
- /* give hook a chance in case parameter is dynamic */
- if (!OidIsValid(prm->ptype) && paramInfo->paramFetch != NULL)
- (*paramInfo->paramFetch) (paramInfo, thisParamId);
+ /*
+ * Now call the function, passing the evaluated parameter values.
+ */
- 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))));
+ /* 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;
- *isNull = prm->isnull;
- return prm->value;
+ /*
+ * 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;
+ }
}
}
- 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 (callit)
+ {
+ pgstat_init_function_usage(fcinfo, &fcusage);
- if (isNull == NULL)
- elog(ERROR, "a NULL isNull pointer was passed");
+ fcinfo->isnull = false;
+ rsinfo.isDone = ExprSingleResult;
+ result = FunctionCallInvoke(fcinfo);
+ *isNull = fcinfo->isnull;
+ *isDone = rsinfo.isDone;
- if (tuple == NULL)
+ pgstat_end_function_usage(&fcusage,
+ rsinfo.isDone != ExprMultipleResult);
+ }
+ else
{
- /* Kinda bogus but compatible with old behavior... */
+ /* for a strict SRF, result for NULL is an empty set */
+ result = (Datum) 0;
*isNull = true;
- return (Datum) 0;
+ *isDone = ExprEndResult;
}
- 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);
+ /* 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,
+ ShutdownSetExpr,
+ 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;
}
-Datum
-GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
+/*
+ * Prepare function call in nodeProjectSet.c (taretlist SRF) for execution.
+ */
+SetExprState *
+ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent)
{
- AttrNumber attrno;
- Datum result;
- Oid tupType;
- int32 tupTypmod;
- TupleDesc tupDesc;
- HeapTupleData tmptup;
- int i;
-
- if (attname == NULL)
- elog(ERROR, "invalid attribute name");
+ SetExprState *state = makeNode(SetExprState);
+ ListCell *lc;
- if (isNull == NULL)
- elog(ERROR, "a NULL isNull pointer was passed");
+ state->funcReturnsSet = true;
+ state->expr = expr;
+ state->func.fn_oid = InvalidOid;
- if (tuple == NULL)
+ /*
+ * Initialize metadata. The expression node could be either a FuncExpr or
+ * an OpExpr.
+ */
+ if (IsA(expr, FuncExpr))
{
- /* Kinda bogus but compatible with old behavior... */
- *isNull = true;
- return (Datum) 0;
- }
-
- tupType = HeapTupleHeaderGetTypeId(tuple);
- tupTypmod = HeapTupleHeaderGetTypMod(tuple);
- tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ FuncExpr *func = (FuncExpr *) expr;
- attrno = InvalidAttrNumber;
- for (i = 0; i < tupDesc->natts; i++)
- {
- if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
+ foreach(lc, func->args)
{
- attrno = tupDesc->attrs[i]->attnum;
- break;
+ state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
}
- }
- if (attrno == InvalidAttrNumber)
- elog(ERROR, "attribute \"%s\" does not exist", attname);
+ init_sexpr(func->funcid, func->inputcollid, state,
+ econtext->ecxt_per_query_memory, true, true);
+ }
+ else if (IsA(expr, OpExpr))
+ {
+ OpExpr *op = (OpExpr *) expr;
- /*
- * 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;
+ foreach(lc, op->args)
+ {
+ state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+ }
- result = heap_getattr(&tmptup,
- attrno,
- tupDesc,
- isNull);
+ init_sexpr(op->opfuncid, op->inputcollid, state,
+ econtext->ecxt_per_query_memory, true, true);
+ }
+ else
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(expr));
- ReleaseTupleDesc(tupDesc);
+ /* shouldn't get here otherwise */
+ Assert(state->func.fn_retset);
- return result;
+ return state;
}
/*
- * init_fcache - initialize a FuncExprState node during first use
+ * Prepare function call in nodeFunctionscan.c (FROM function/ROWS FROM) for
+ * execution.
*/
-static void
-init_fcache(Oid foid, Oid input_collation, FuncExprState *fcache,
- MemoryContext fcacheCxt, bool allowSRF, bool needDescForSRF)
+SetExprState *
+ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent)
{
- AclResult aclresult;
+ SetExprState *state = makeNode(SetExprState);
+ ListCell *lc;
- /* 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);
+ state->funcReturnsSet = false;
+ state->expr = expr;
+ state->func.fn_oid = InvalidOid;
/*
- * 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?
+ * Normally the passed expression tree will be a FuncExpr, 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 (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 (IsA(expr, FuncExpr))
+ {
+ FuncExpr *func = (FuncExpr *) expr;
- /* 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")));
+ state->funcReturnsSet = func->funcretset;
- /* Otherwise, ExecInitExpr should have marked the fcache correctly */
- Assert(fcache->func.fn_retset == fcache->funcReturnsSet);
+ foreach(lc, func->args)
+ {
+ state->args = lappend(state->args, ExecInitExpr(lfirst(lc), parent));
+ }
- /* If function returns set, prepare expected tuple descriptor */
- if (fcache->func.fn_retset && needDescForSRF)
+ init_sexpr(func->funcid, func->inputcollid, state,
+ econtext->ecxt_per_query_memory, func->funcretset, false);
+ }
+ else
{
- TypeFuncClass functypclass;
- Oid funcrettype;
- TupleDesc tupdesc;
- MemoryContext oldcontext;
+ state->elidedFuncState = ExecInitExpr(expr, parent);
+ }
- 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;
+ return state;
+}
- 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);
+/*
+ * ExecMakeTableFunctionResult
+ *
+ * Evaluate a table function, producing a materialized result in a Tuplestore
+ * object.
+ */
+Tuplestorestate *
+ExecMakeTableFunctionResult(SetExprState *setexpr,
+ 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 first_time = true;
- /*
- * 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);
+ callerContext = CurrentMemoryContext;
- 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);
+ funcrettype = exprType((Node *) setexpr->expr);
- 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);
+ returnsTuple = type_is_rowtype(funcrettype);
- 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);
+ /*
+ * 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;
- 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);
+ /*
+ * Normally the passed expression tree will be a SetExprState, 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 (!setexpr->elidedFuncState)
+ {
+ /*
+ * This path is similar to ExecMakeFunctionResultSet.
+ */
+ returnsSet = setexpr->funcReturnsSet;
+ InitFunctionCallInfoData(fcinfo, &(setexpr->func),
+ list_length(setexpr->args),
+ setexpr->fcinfo_data.fncollation,
+ NULL, (Node *) &rsinfo);
- 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);
+ /*
+ * 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, setexpr->args, econtext);
+ MemoryContextSwitchTo(oldcontext);
- 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);
+ /*
+ * 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 (setexpr->func.fn_strict)
+ {
+ int i;
- 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:
+ for (i = 0; i < fcinfo.nargs; i++)
{
- 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;
+ if (fcinfo.argnull[i])
+ goto no_function_result;
}
- break;
- case T_AlternativeSubPlan:
- {
- AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
- AlternativeSubPlanState *asstate;
+ }
+ }
+ else
+ {
+ /* Treat setexpr as a generic expression */
+ InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
+ }
- if (!parent)
- elog(ERROR, "AlternativeSubPlan found with no parent plan");
+ /*
+ * Switch to short-lived context for calling the function or expression.
+ */
+ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
- asstate = ExecInitAlternativeSubPlan(asplan, parent);
+ /*
+ * Loop to handle the ValuePerCall protocol (which is also the same
+ * behavior needed in the generic ExecEvalExpr path).
+ */
+ for (;;)
+ {
+ Datum result;
- state = (ExprState *) asstate;
- }
- break;
- case T_FieldSelect:
- {
- FieldSelect *fselect = (FieldSelect *) node;
- FieldSelectState *fstate = makeNode(FieldSelectState);
+ CHECK_FOR_INTERRUPTS();
- 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);
+ /*
+ * 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);
- 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);
+ /* Call the function or expression one time */
+ if (!setexpr->elidedFuncState)
+ {
+ pgstat_init_function_usage(&fcinfo, &fcusage);
- 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;
+ fcinfo.isnull = false;
+ rsinfo.isDone = ExprSingleResult;
+ result = FunctionCallInvoke(&fcinfo);
- 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);
+ pgstat_end_function_usage(&fcusage,
+ rsinfo.isDone != ExprMultipleResult);
+ }
+ else
+ {
+ result =
+ ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
+ rsinfo.isDone = ExprSingleResult;
+ }
- 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);
+ /* Which protocol does function want to use? */
+ if (rsinfo.returnMode == SFRM_ValuePerCall)
+ {
+ /*
+ * Check for end of result set.
+ */
+ if (rsinfo.isDone == ExprEndResult)
+ break;
- cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
- cstate->arg = ExecInitExpr(convert->arg, parent);
- state = (ExprState *) cstate;
- }
- break;
- case T_CaseExpr:
+ /*
+ * If first time through, build tuplestore for result. For a
+ * scalar function result type, also make a suitable tupdesc.
+ */
+ if (first_time)
{
- 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)
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
+ rsinfo.setResult = tupstore;
+ if (!returnsTuple)
{
- 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);
+ tupdesc = CreateTemplateTupleDesc(1, false);
+ TupleDescInitEntry(tupdesc,
+ (AttrNumber) 1,
+ "column",
+ funcrettype,
+ -1,
+ 0);
+ rsinfo.setDesc = tupdesc;
}
- cstate->args = outlist;
- cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
- if (caseexpr->arg)
- cstate->argtyplen = get_typlen(exprType((Node *) caseexpr->arg));
- state = (ExprState *) cstate;
+ MemoryContextSwitchTo(oldcontext);
}
- 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:
+ /*
+ * Store current resultset item.
+ */
+ if (returnsTuple)
{
- 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)
+ if (!fcinfo.isnull)
{
- Expr *e = (Expr *) lfirst(l);
- ExprState *estate;
+ HeapTupleHeader td = DatumGetHeapTupleHeader(result);
- if (!attrs[i]->attisdropped)
+ if (tupdesc == NULL)
{
/*
- * 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.
+ * 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.
*/
- 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))));
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
+ HeapTupleHeaderGetTypMod(td));
+ rsinfo.setDesc = tupdesc;
+ MemoryContextSwitchTo(oldcontext);
}
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.
+ * Verify all later returned rows have same subtype;
+ * necessary in case the type is RECORD.
*/
- e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
+ 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")));
}
- 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.
+ * tuplestore_puttuple needs a HeapTuple not a bare
+ * HeapTupleHeader, but it doesn't need all the fields.
*/
- 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;
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ tmptup.t_data = td;
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
+ tuplestore_puttuple(tupstore, &tmptup);
}
- xstate->named_args = outlist;
-
- outlist = NIL;
- foreach(arg, xexpr->args)
+ else
{
- Expr *e = (Expr *) lfirst(arg);
- ExprState *estate;
+ /*
+ * 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;
- estate = ExecInitExpr(e, parent);
- outlist = lappend(outlist, estate);
+ nullflags = (bool *) palloc(natts * sizeof(bool));
+ memset(nullflags, true, natts * sizeof(bool));
+ tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
}
- 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:
+ else
{
- 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;
+ /* Scalar-type case: just store the function result */
+ tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
}
- 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;
- }
+ /*
+ * 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;
- case T_List:
- {
- List *outlist = NIL;
- ListCell *l;
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
+ errmsg("unrecognized table-function returnMode: %d",
+ (int) rsinfo.returnMode)));
- 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;
+ first_time = false;
}
- /* 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;
+no_function_result:
/*
- * debugging stuff
+ * 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.
*/
- EV_printf("ExecQual: qual is ");
- EV_nodeDisplay(qual);
- EV_printf("\n");
+ 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;
- /*
- * Run in short-lived per-tuple context while computing expressions.
- */
- oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ nullflags = (bool *) palloc(natts * sizeof(bool));
+ memset(nullflags, true, natts * sizeof(bool));
+ tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
+ }
+ }
/*
- * 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.
+ * 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.
*/
- result = true;
-
- foreach(l, qual)
+ if (rsinfo.setDesc)
{
- ExprState *clause = (ExprState *) lfirst(l);
- Datum expr_value;
- bool isNull;
-
- expr_value = ExecEvalExpr(clause, econtext, &isNull);
+ tupledesc_match(expectedDesc, rsinfo.setDesc);
- if (isNull)
- {
- if (resultForNull == false)
- {
- result = false; /* treat NULL as FALSE */
- break;
- }
- }
- else
- {
- if (!DatumGetBool(expr_value))
- {
- result = false; /* definitely FALSE */
- break;
- }
- }
+ /*
+ * 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(oldContext);
+ MemoryContextSwitchTo(callerContext);
- return result;
+ /* All done, pass back the tuplestore */
+ return rsinfo.setResult;
}
/*
foreach(tl, targetlist)
{
- TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
+ TargetEntry *curTle = (TargetEntry *) lfirst(tl);
+ Assert(IsA(curTle, TargetEntry));
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);
-}
ExecScanRecheckMtd recheckMtd)
{
ExprContext *econtext;
- List *qual;
+ ExprState *qual;
ProjectionInfo *projInfo;
/*
if (TupIsNull(slot))
{
if (projInfo)
- return ExecClearTuple(projInfo->pi_slot);
+ return ExecClearTuple(projInfo->pi_state.resultslot);
else
return slot;
}
* when the qual is nil ... saves only a few cycles, but they add up
* ...
*/
- if (!qual || ExecQual(qual, econtext, false))
+ if (!qual || ExecQual(qual, econtext))
{
/*
* Found a satisfactory scan tuple.
return slot->tts_tupleDescriptor;
}
-/* ----------------
- * ExecBuildProjectionInfo
- *
- * Build a ProjectionInfo node for evaluating the given tlist in the given
- * econtext, and storing the result into the tuple slot. (Caller must have
- * ensured that tuple slot has a descriptor matching the tlist!) Note that
- * the given tlist should be a list of ExprState nodes, not Expr nodes.
- *
- * inputDesc can be NULL, but if it is not, we check to see whether simple
- * Vars in the tlist match the descriptor. It is important to provide
- * inputDesc for relation-scan plan nodes, as a cross check that the relation
- * hasn't been changed since the plan was made. At higher levels of a plan,
- * there is no need to recheck.
- * ----------------
- */
-ProjectionInfo *
-ExecBuildProjectionInfo(List *targetList,
- ExprContext *econtext,
- TupleTableSlot *slot,
- TupleDesc inputDesc)
-{
- ProjectionInfo *projInfo = makeNode(ProjectionInfo);
- int len = ExecTargetListLength(targetList);
- int *workspace;
- int *varSlotOffsets;
- int *varNumbers;
- int *varOutputCols;
- List *exprlist;
- int numSimpleVars;
- bool directMap;
- ListCell *tl;
-
- projInfo->pi_exprContext = econtext;
- projInfo->pi_slot = slot;
- /* since these are all int arrays, we need do just one palloc */
- workspace = (int *) palloc(len * 3 * sizeof(int));
- projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
- projInfo->pi_varNumbers = varNumbers = workspace + len;
- projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
- projInfo->pi_lastInnerVar = 0;
- projInfo->pi_lastOuterVar = 0;
- projInfo->pi_lastScanVar = 0;
-
- /*
- * We separate the target list elements into simple Var references and
- * expressions which require the full ExecTargetList machinery. To be a
- * simple Var, a Var has to be a user attribute and not mismatch the
- * inputDesc. (Note: if there is a type mismatch then ExecEvalScalarVar
- * will probably throw an error at runtime, but we leave that to it.)
- */
- exprlist = NIL;
- numSimpleVars = 0;
- directMap = true;
- foreach(tl, targetList)
- {
- GenericExprState *gstate = (GenericExprState *) lfirst(tl);
- Var *variable = (Var *) gstate->arg->expr;
- bool isSimpleVar = false;
-
- if (variable != NULL &&
- IsA(variable, Var) &&
- variable->varattno > 0)
- {
- if (!inputDesc)
- isSimpleVar = true; /* can't check type, assume OK */
- else if (variable->varattno <= inputDesc->natts)
- {
- Form_pg_attribute attr;
-
- attr = inputDesc->attrs[variable->varattno - 1];
- if (!attr->attisdropped && variable->vartype == attr->atttypid)
- isSimpleVar = true;
- }
- }
-
- if (isSimpleVar)
- {
- TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
- AttrNumber attnum = variable->varattno;
-
- varNumbers[numSimpleVars] = attnum;
- varOutputCols[numSimpleVars] = tle->resno;
- if (tle->resno != numSimpleVars + 1)
- directMap = false;
-
- switch (variable->varno)
- {
- case INNER_VAR:
- varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
- ecxt_innertuple);
- if (projInfo->pi_lastInnerVar < attnum)
- projInfo->pi_lastInnerVar = attnum;
- break;
-
- case OUTER_VAR:
- varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
- ecxt_outertuple);
- if (projInfo->pi_lastOuterVar < attnum)
- projInfo->pi_lastOuterVar = attnum;
- break;
-
- /* INDEX_VAR is handled by default case */
-
- default:
- varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
- ecxt_scantuple);
- if (projInfo->pi_lastScanVar < attnum)
- projInfo->pi_lastScanVar = attnum;
- break;
- }
- numSimpleVars++;
- }
- else
- {
- /* Not a simple variable, add it to generic targetlist */
- exprlist = lappend(exprlist, gstate);
- /* Examine expr to include contained Vars in lastXXXVar counts */
- ExecGetLastAttnums((Node *) variable,
- &projInfo->pi_lastOuterVar,
- &projInfo->pi_lastInnerVar,
- &projInfo->pi_lastScanVar);
- }
- }
- projInfo->pi_targetlist = exprlist;
- projInfo->pi_numSimpleVars = numSimpleVars;
- projInfo->pi_directMap = directMap;
-
- return projInfo;
-}
-
/*
* get_last_attnums_walker: expression walker for ExecBuildProjectionInfo
*
TupleDesc inputDesc)
{
planstate->ps_ProjInfo =
- ExecBuildProjectionInfo(planstate->targetlist,
+ ExecBuildProjectionInfo(planstate->plan->targetlist,
planstate->ps_ExprContext,
planstate->ps_ResultTupleSlot,
+ planstate,
inputDesc);
}
/*
* Check the qual (HAVING clause); if the group does not match, ignore it.
*/
- if (ExecQual(aggstate->ss.ps.qual, econtext, false))
+ if (ExecQual(aggstate->ss.ps.qual, econtext))
{
/*
* Form and return projection tuple using the aggregate results and
* under SQL semantics anyway (and it's forbidden by the spec). Because
* that is true, we don't need to worry about evaluating the aggs in any
* particular order.
+ * FIXME: adjust comment (now added in projection)
*/
- aggstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) aggstate);
- aggstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) aggstate);
+ aggstate->ss.ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) aggstate);
/*
* Initialize child nodes.
foreach(l, aggstate->aggs)
{
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
- Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
+ Aggref *aggref = aggrefstate->aggref;
AggStatePerAgg peragg;
AggStatePerTrans pertrans;
int existing_aggno;
/* and then create a projection for that targetlist */
aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false);
aggstate->evalslot = ExecInitExtraTupleSlot(estate);
- combined_inputeval = (List *) ExecInitExpr((Expr *) combined_inputeval,
- (PlanState *) aggstate);
aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval,
aggstate->tmpcontext,
aggstate->evalslot,
+ &aggstate->ss.ps,
NULL);
ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc);
naggs = aggstate->numaggs;
pertrans->aggfilter = ExecInitExpr(aggref->aggfilter,
(PlanState *) aggstate);
- pertrans->aggdirectargs = (List *) ExecInitExpr((Expr *) aggref->aggdirectargs,
- (PlanState *) aggstate);
+ pertrans->aggdirectargs = ExecInitExprList(aggref->aggdirectargs,
+ (PlanState *) aggstate);
/*
* Complain if the aggregate's arguments contain any aggregates; nested
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
- if (!ExecQual(node->bitmapqualorig, econtext, false))
+ if (!ExecQual(node->bitmapqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
ResetExprContext(econtext);
- return ExecQual(node->bitmapqualorig, econtext, false);
+ return ExecQual(node->bitmapqualorig, econtext);
}
/* ----------------------------------------------------------------
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
- scanstate->bitmapqualorig = (List *)
- ExecInitExpr((Expr *) node->bitmapqualorig,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+ scanstate->bitmapqualorig =
+ ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
/*
* tuple table initialization
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization
ExecAssignExprContext(estate, &css->ss.ps);
/* initialize child expressions */
- css->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
- (PlanState *) css);
- css->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) cscan->scan.plan.qual,
+ css->ss.ps.qual =
+ ExecInitQual(cscan->scan.plan.qual,
(PlanState *) css);
/* tuple table initialization */
!fdwroutine->RecheckForeignScan(node, slot))
return false;
- return ExecQual(node->fdw_recheck_quals, econtext, false);
+ return ExecQual(node->fdw_recheck_quals, econtext);
}
/* ----------------------------------------------------------------
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
- scanstate->fdw_recheck_quals = (List *)
- ExecInitExpr((Expr *) node->fdw_recheck_quals,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+ scanstate->fdw_recheck_quals =
+ ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
/*
* tuple table initialization
*/
typedef struct FunctionScanPerFuncState
{
- ExprState *funcexpr; /* state of the expression being evaluated */
+ SetExprState *setexpr; /* state of the expression being evaluated */
TupleDesc tupdesc; /* desc of the function result type */
int colcount; /* expected number of result columns */
Tuplestorestate *tstore; /* holds the function result set */
if (tstore == NULL)
{
node->funcstates[0].tstore = tstore =
- ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
+ ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
node->ss.ps.ps_ExprContext,
node->argcontext,
node->funcstates[0].tupdesc,
if (fs->tstore == NULL)
{
fs->tstore =
- ExecMakeTableFunctionResult(fs->funcexpr,
+ ExecMakeTableFunctionResult(fs->setexpr,
node->ss.ps.ps_ExprContext,
node->argcontext,
fs->tupdesc,
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
Oid funcrettype;
TupleDesc tupdesc;
- fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
+ fs->setexpr =
+ ExecInitTableFunctionResult((Expr *) funcexpr,
+ scanstate->ss.ps.ps_ExprContext,
+ &scanstate->ss.ps);
/*
* Don't allocate the tuplestores; the actual calls to the functions
/*
* initialize child expressions
*/
- gatherstate->ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) gatherstate);
- gatherstate->ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) gatherstate);
+ gatherstate->ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) gatherstate);
/*
* tuple table initialization
/*
* initialize child expressions
*/
- gm_state->ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) gm_state);
- gm_state->ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) gm_state);
+ gm_state->ps.qual =
+ ExecInitQual(node->plan.qual,
+ &gm_state->ps);
/*
* tuple table initialization
* Check the qual (HAVING clause); if the group does not match, ignore
* it and fall into scan loop.
*/
- if (ExecQual(node->ss.ps.qual, econtext, false))
+ if (ExecQual(node->ss.ps.qual, econtext))
{
/*
* Form and return a projection tuple using the first input tuple.
* Check the qual (HAVING clause); if the group does not match, ignore
* it and loop back to scan the rest of the group.
*/
- if (ExecQual(node->ss.ps.qual, econtext, false))
+ if (ExecQual(node->ss.ps.qual, econtext))
{
/*
* Form and return a projection tuple using the first input tuple.
/*
* initialize child expressions
*/
- grpstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) grpstate);
- grpstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) grpstate);
+ grpstate->ss.ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) grpstate);
/*
* initialize child nodes
/*
* initialize child expressions
*/
- hashstate->ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) hashstate);
- hashstate->ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) hashstate);
+ hashstate->ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) hashstate);
/*
* initialize child nodes
ExecScanHashBucket(HashJoinState *hjstate,
ExprContext *econtext)
{
- List *hjclauses = hjstate->hashclauses;
+ ExprState *hjclauses = hjstate->hashclauses;
HashJoinTable hashtable = hjstate->hj_HashTable;
HashJoinTuple hashTuple = hjstate->hj_CurTuple;
uint32 hashvalue = hjstate->hj_CurHashValue;
/* reset temp memory each time to avoid leaks from qual expr */
ResetExprContext(econtext);
- if (ExecQual(hjclauses, econtext, false))
+ if (ExecQual(hjclauses, econtext))
{
hjstate->hj_CurTuple = hashTuple;
return true;
{
PlanState *outerNode;
HashState *hashNode;
- List *joinqual;
- List *otherqual;
+ ExprState *joinqual;
+ ExprState *otherqual;
ExprContext *econtext;
HashJoinTable hashtable;
TupleTableSlot *outerTupleSlot;
* Only the joinquals determine tuple match status, but all
* quals must pass to actually return the tuple.
*/
- if (joinqual == NIL || ExecQual(joinqual, econtext, false))
+ if (joinqual == NULL || ExecQual(joinqual, econtext))
{
node->hj_MatchedOuter = true;
HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
if (node->js.jointype == JOIN_SEMI)
node->hj_JoinState = HJ_NEED_NEW_OUTER;
- if (otherqual == NIL ||
- ExecQual(otherqual, econtext, false))
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
*/
econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
- if (otherqual == NIL ||
- ExecQual(otherqual, econtext, false))
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
*/
econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
- if (otherqual == NIL ||
- ExecQual(otherqual, econtext, false))
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
/*
* initialize child expressions
*/
- hjstate->js.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->join.plan.targetlist,
- (PlanState *) hjstate);
- hjstate->js.ps.qual = (List *)
- ExecInitExpr((Expr *) node->join.plan.qual,
- (PlanState *) hjstate);
+ hjstate->js.ps.qual =
+ ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
hjstate->js.jointype = node->join.jointype;
- hjstate->js.joinqual = (List *)
- ExecInitExpr((Expr *) node->join.joinqual,
- (PlanState *) hjstate);
- hjstate->hashclauses = (List *)
- ExecInitExpr((Expr *) node->hashclauses,
- (PlanState *) hjstate);
+ hjstate->js.joinqual =
+ ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
+ hjstate->hashclauses =
+ ExecInitQual(node->hashclauses, (PlanState *) hjstate);
/*
* initialize child nodes
lclauses = NIL;
rclauses = NIL;
hoperators = NIL;
- foreach(l, hjstate->hashclauses)
+ foreach(l, node->hashclauses)
{
- FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
- OpExpr *hclause = castNode(OpExpr, fstate->xprstate.expr);
+ OpExpr *hclause = castNode(OpExpr, lfirst(l));
+
+ lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args), (PlanState *) hjstate));
+ rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args), (PlanState *) hjstate));
- lclauses = lappend(lclauses, linitial(fstate->args));
- rclauses = lappend(rclauses, lsecond(fstate->args));
hoperators = lappend_oid(hoperators, hclause->opno);
}
hjstate->hj_OuterHashKeys = lclauses;
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
- if (!ExecQual(node->indexqual, econtext, false))
+ if (!ExecQual(node->indexqual, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
* Note: we don't initialize all of the indexorderby expression, only the
* sub-parts corresponding to runtime keys (see below).
*/
- indexstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) indexstate);
- indexstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) indexstate);
- indexstate->indexqual = (List *)
- ExecInitExpr((Expr *) node->indexqual,
- (PlanState *) indexstate);
+ indexstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+ indexstate->indexqual =
+ ExecInitQual(node->indexqual, (PlanState *) indexstate);
/*
* tuple table initialization
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
- if (!ExecQual(node->indexqualorig, econtext, false))
+ if (!ExecQual(node->indexqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
- if (!ExecQual(node->indexqualorig, econtext, false))
+ if (!ExecQual(node->indexqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
ResetExprContext(econtext);
- return ExecQual(node->indexqualorig, econtext, false);
+ return ExecQual(node->indexqualorig, econtext);
}
* would be nice to improve that. (Problem is that any SubPlans present
* in the expression must be found now...)
*/
- indexstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) indexstate);
- indexstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) indexstate);
- indexstate->indexqualorig = (List *)
- ExecInitExpr((Expr *) node->indexqualorig,
- (PlanState *) indexstate);
- indexstate->indexorderbyorig = (List *)
- ExecInitExpr((Expr *) node->indexorderbyorig,
- (PlanState *) indexstate);
+ indexstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
+ indexstate->indexqualorig =
+ ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
+ indexstate->indexorderbyorig =
+ ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
/*
* tuple table initialization
MJFillOuter(MergeJoinState *node)
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
- List *otherqual = node->js.ps.qual;
+ ExprState *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
- if (ExecQual(otherqual, econtext, false))
+ if (ExecQual(otherqual, econtext))
{
/*
* qualification succeeded. now form the desired projection tuple and
MJFillInner(MergeJoinState *node)
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
- List *otherqual = node->js.ps.qual;
+ ExprState *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
- if (ExecQual(otherqual, econtext, false))
+ if (ExecQual(otherqual, econtext))
{
/*
* qualification succeeded. now form the desired projection tuple and
TupleTableSlot *
ExecMergeJoin(MergeJoinState *node)
{
- List *joinqual;
- List *otherqual;
+ ExprState *joinqual;
+ ExprState *otherqual;
bool qualResult;
int compareResult;
PlanState *innerPlan;
innerTupleSlot = node->mj_InnerTupleSlot;
econtext->ecxt_innertuple = innerTupleSlot;
- qualResult = (joinqual == NIL ||
- ExecQual(joinqual, econtext, false));
+ qualResult = (joinqual == NULL ||
+ ExecQual(joinqual, econtext));
MJ_DEBUG_QUAL(joinqual, qualResult);
if (qualResult)
if (node->js.jointype == JOIN_SEMI)
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
- qualResult = (otherqual == NIL ||
- ExecQual(otherqual, econtext, false));
+ qualResult = (otherqual == NULL ||
+ ExecQual(otherqual, econtext));
MJ_DEBUG_QUAL(otherqual, qualResult);
if (qualResult)
/*
* initialize child expressions
*/
- mergestate->js.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->join.plan.targetlist,
- (PlanState *) mergestate);
- mergestate->js.ps.qual = (List *)
- ExecInitExpr((Expr *) node->join.plan.qual,
- (PlanState *) mergestate);
+ mergestate->js.ps.qual =
+ ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
mergestate->js.jointype = node->join.jointype;
- mergestate->js.joinqual = (List *)
- ExecInitExpr((Expr *) node->join.joinqual,
- (PlanState *) mergestate);
+ mergestate->js.joinqual =
+ ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
mergestate->mj_ConstFalseJoin = false;
/* mergeclauses are handled below */
{
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
- List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
+ ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
HeapTupleData tuple;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
econtext->ecxt_innertuple = excludedSlot;
econtext->ecxt_outertuple = NULL;
- if (!ExecQual(onConflictSetWhere, econtext, false))
+ if (!ExecQual(onConflictSetWhere, econtext))
{
ReleaseBuffer(buffer);
InstrCountFiltered1(&mtstate->ps, 1);
mtstate = makeNode(ModifyTableState);
mtstate->ps.plan = (Plan *) node;
mtstate->ps.state = estate;
- mtstate->ps.targetlist = NIL; /* not actually used */
mtstate->operation = operation;
mtstate->canSetTag = node->canSetTag;
foreach(ll, wcoList)
{
WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
- ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
- mtstate->mt_plans[i]);
+ ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
+ mtstate->mt_plans[i]);
wcoExprs = lappend(wcoExprs, wcoExpr);
}
foreach(ll, mapped_wcoList)
{
WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
- ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
+ ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
mtstate->mt_plans[i]);
wcoExprs = lappend(wcoExprs, wcoExpr);
slot = mtstate->ps.ps_ResultTupleSlot;
/* Need an econtext too */
- econtext = CreateExprContext(estate);
- mtstate->ps.ps_ExprContext = econtext;
+ if (mtstate->ps.ps_ExprContext == NULL)
+ ExecAssignExprContext(estate, &mtstate->ps);
+ econtext = mtstate->ps.ps_ExprContext;
/*
* Build a projection for each result rel.
foreach(l, node->returningLists)
{
List *rlist = (List *) lfirst(l);
- List *rliststate;
- rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
resultRelInfo->ri_projectReturning =
- ExecBuildProjectionInfo(rliststate, econtext, slot,
+ ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
for (i = 0; i < mtstate->mt_num_partitions; i++)
{
Relation partrel = resultRelInfo->ri_RelationDesc;
- List *rlist,
- *rliststate;
+ List *rlist;
/* varno = node->nominalRelation */
rlist = map_partition_varattnos(returningList,
node->nominalRelation,
partrel, rel);
- rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
resultRelInfo->ri_projectReturning =
- ExecBuildProjectionInfo(rliststate, econtext, slot,
- resultRelInfo->ri_RelationDesc->rd_att);
+ ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
+ resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
}
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
ExprContext *econtext;
- ExprState *setexpr;
TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
- /* build UPDATE SET expression and projection state */
- setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
+ /* build UPDATE SET projection state */
resultRelInfo->ri_onConflictSetProj =
- ExecBuildProjectionInfo((List *) setexpr, econtext,
- mtstate->mt_conflproj,
+ ExecBuildProjectionInfo(node->onConflictSet, econtext,
+ mtstate->mt_conflproj, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
/* build DO UPDATE WHERE clause expression */
{
ExprState *qualexpr;
- qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
+ qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
- resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
+ resultRelInfo->ri_onConflictSetWhere = qualexpr;
}
}
PlanState *outerPlan;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
- List *joinqual;
- List *otherqual;
+ ExprState *joinqual;
+ ExprState *otherqual;
ExprContext *econtext;
ListCell *lc;
ENL1_printf("testing qualification for outer-join tuple");
- if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
{
/*
* qualification was satisfied so we project and return
*/
ENL1_printf("testing qualification");
- if (ExecQual(joinqual, econtext, false))
+ if (ExecQual(joinqual, econtext))
{
node->nl_MatchedOuter = true;
if (node->js.jointype == JOIN_SEMI)
node->nl_NeedNewOuter = true;
- if (otherqual == NIL || ExecQual(otherqual, econtext, false))
+ if (otherqual == NULL || ExecQual(otherqual, econtext))
{
/*
* qualification was satisfied so we project and return the
/*
* initialize child expressions
*/
- nlstate->js.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->join.plan.targetlist,
- (PlanState *) nlstate);
- nlstate->js.ps.qual = (List *)
- ExecInitExpr((Expr *) node->join.plan.qual,
- (PlanState *) nlstate);
+ nlstate->js.ps.qual =
+ ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
nlstate->js.jointype = node->join.jointype;
- nlstate->js.joinqual = (List *)
- ExecInitExpr((Expr *) node->join.joinqual,
- (PlanState *) nlstate);
+ nlstate->js.joinqual =
+ ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
/*
* initialize child nodes
#include "executor/executor.h"
#include "executor/nodeProjectSet.h"
+#include "nodes/nodeFuncs.h"
#include "utils/memutils.h"
bool hassrf PG_USED_FOR_ASSERTS_ONLY = false;
bool hasresult;
int argno;
- ListCell *lc;
ExecClearTuple(resultSlot);
node->pending_srf_tuples = false;
hasresult = false;
- argno = 0;
- foreach(lc, node->ps.targetlist)
+ for (argno = 0; argno < node->nelems; argno++)
{
- GenericExprState *gstate = (GenericExprState *) lfirst(lc);
+ Node *elem = node->elems[argno];
ExprDoneCond *isdone = &node->elemdone[argno];
Datum *result = &resultSlot->tts_values[argno];
bool *isnull = &resultSlot->tts_isnull[argno];
*isnull = true;
hassrf = true;
}
- else if (IsA(gstate->arg, FuncExprState) &&
- ((FuncExprState *) gstate->arg)->funcReturnsSet)
+ else if (IsA(elem, SetExprState))
{
/*
* Evaluate SRF - possibly continuing previously started output.
*/
- *result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
+ *result = ExecMakeFunctionResultSet((SetExprState *) elem,
econtext, isnull, isdone);
if (*isdone != ExprEndResult)
else
{
/* Non-SRF tlist expression, just evaluate normally. */
- *result = ExecEvalExpr(gstate->arg, econtext, isnull);
+ *result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
*isdone = ExprSingleResult;
}
- argno++;
}
/* ProjectSet should not be used if there's no SRFs */
ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
{
ProjectSetState *state;
+ ListCell *lc;
+ int off;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
*/
ExecInitResultTupleSlot(estate, &state->ps);
- /*
- * initialize child expressions
- */
- state->ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) state);
Assert(node->plan.qual == NIL);
/*
/* Create workspace for per-SRF is-done state */
state->nelems = list_length(node->plan.targetlist);
+ state->elems = (Node **)
+ palloc(sizeof(Node *) * state->nelems);
state->elemdone = (ExprDoneCond *)
palloc(sizeof(ExprDoneCond) * state->nelems);
+ /*
+ * Build expressions to evaluate targetlist. Can't use
+ * ExecBuildProjectionInfo here, since that doesn't deal with
+ * SRFs. Instead evaluate all expressions individually, using
+ * ExecInitFunctionResultSet where applicable.
+ */
+ off = 0;
+ foreach(lc, node->plan.targetlist)
+ {
+ TargetEntry *te = (TargetEntry *) lfirst(lc);
+ Expr *expr = te->expr;
+
+ if ((IsA(expr, FuncExpr) && ((FuncExpr *) expr)->funcretset) ||
+ (IsA(expr, OpExpr) && ((OpExpr *) expr)->opretset))
+ {
+ state->elems[off] = (Node *)
+ ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
+ &state->ps);
+ }
+ else
+ {
+ state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
+ Assert(!expression_returns_set((Node *) expr));
+ }
+
+ off++;
+ }
return state;
}
*/
if (node->rs_checkqual)
{
- bool qualResult = ExecQual((List *) node->resconstantqual,
- econtext,
- false);
+ bool qualResult = ExecQual(node->resconstantqual, econtext);
node->rs_checkqual = false;
if (!qualResult)
/*
* initialize child expressions
*/
- resstate->ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) resstate);
- resstate->ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) resstate);
- resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
- (PlanState *) resstate);
+ resstate->ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) resstate);
+ resstate->resconstantqual =
+ ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
/*
* initialize child nodes
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
-
- scanstate->args = (List *)
- ExecInitExpr((Expr *) tsc->args,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
+
+ scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
scanstate->repeatable =
- ExecInitExpr(tsc->repeatable,
- (PlanState *) scanstate);
+ ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
/*
* tuple table initialization
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->plan.qual,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization
#include "utils/memutils.h"
-static Datum ExecSubPlan(SubPlanState *node,
- ExprContext *econtext,
- bool *isNull);
-static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
- ExprContext *econtext,
- bool *isNull);
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
* This is the main entry point for execution of a regular SubPlan.
* ----------------------------------------------------------------
*/
-static Datum
+Datum
ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull)
{
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
/* Set non-null as default */
*isNull = false;
ExprContext *econtext,
bool *isNull)
{
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
TupleTableSlot *slot;
ExprContext *econtext,
bool *isNull)
{
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
SubLinkType subLinkType = subplan->subLinkType;
MemoryContext oldcontext;
static void
buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
{
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
int ncols = list_length(subplan->paramIds);
ExprContext *innerecontext = node->innerecontext;
* potential for a double free attempt. (XXX possibly no longer needed,
* but can't hurt.)
*/
- ExecClearTuple(node->projRight->pi_slot);
+ ExecClearTuple(node->projRight->pi_state.resultslot);
MemoryContextSwitchTo(oldcontext);
}
SubPlanState *sstate = makeNode(SubPlanState);
EState *estate = parent->state;
- sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
- sstate->xprstate.expr = (Expr *) subplan;
+ sstate->subplan = subplan;
/* Link the SubPlanState to already-initialized subplan */
sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
/* Initialize subexpressions */
sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
- sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
+ sstate->args = ExecInitExprList(subplan->args, parent);
/*
* initialize my state
TupleTableSlot *slot;
List *oplist,
*lefttlist,
- *righttlist,
- *leftptlist,
- *rightptlist;
+ *righttlist;
ListCell *l;
/* We need a memory context to hold the hash table(s) */
* We also extract the combining operators themselves to initialize
* the equality and hashing functions for the hash tables.
*/
- if (IsA(sstate->testexpr->expr, OpExpr))
+ if (IsA(subplan->testexpr, OpExpr))
{
/* single combining operator */
- oplist = list_make1(sstate->testexpr);
+ oplist = list_make1(subplan->testexpr);
}
- else if (and_clause((Node *) sstate->testexpr->expr))
+ else if (and_clause((Node *) subplan->testexpr))
{
/* multiple combining operators */
- oplist = castNode(BoolExprState, sstate->testexpr)->args;
+ Assert(IsA(subplan->testexpr, BoolExpr));
+ oplist = castNode(BoolExpr, subplan->testexpr)->args;
}
else
{
/* shouldn't see anything else in a hashable subplan */
elog(ERROR, "unrecognized testexpr type: %d",
- (int) nodeTag(sstate->testexpr->expr));
+ (int) nodeTag(subplan->testexpr));
oplist = NIL; /* keep compiler quiet */
}
Assert(list_length(oplist) == ncols);
lefttlist = righttlist = NIL;
- leftptlist = rightptlist = NIL;
sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
i = 1;
foreach(l, oplist)
{
- FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
- OpExpr *opexpr = castNode(OpExpr, fstate->xprstate.expr);
- ExprState *exstate;
+ OpExpr *opexpr = castNode(OpExpr, lfirst(l));
Expr *expr;
TargetEntry *tle;
- GenericExprState *tlestate;
Oid rhs_eq_oper;
Oid left_hashfn;
Oid right_hashfn;
- Assert(list_length(fstate->args) == 2);
+ Assert(list_length(opexpr->args) == 2);
/* Process lefthand argument */
- exstate = (ExprState *) linitial(fstate->args);
- expr = exstate->expr;
+ expr = (Expr *) linitial(opexpr->args);
tle = makeTargetEntry(expr,
i,
NULL,
false);
- tlestate = makeNode(GenericExprState);
- tlestate->xprstate.expr = (Expr *) tle;
- tlestate->xprstate.evalfunc = NULL;
- tlestate->arg = exstate;
- lefttlist = lappend(lefttlist, tlestate);
- leftptlist = lappend(leftptlist, tle);
+ lefttlist = lappend(lefttlist, tle);
/* Process righthand argument */
- exstate = (ExprState *) lsecond(fstate->args);
- expr = exstate->expr;
+ expr = (Expr *) lsecond(opexpr->args);
tle = makeTargetEntry(expr,
i,
NULL,
false);
- tlestate = makeNode(GenericExprState);
- tlestate->xprstate.expr = (Expr *) tle;
- tlestate->xprstate.evalfunc = NULL;
- tlestate->arg = exstate;
- righttlist = lappend(righttlist, tlestate);
- rightptlist = lappend(rightptlist, tle);
+ righttlist = lappend(righttlist, tle);
/* Lookup the equality function (potentially cross-type) */
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
* (hack alert!). The righthand expressions will be evaluated in our
* own innerecontext.
*/
- tupDesc = ExecTypeFromTL(leftptlist, false);
+ tupDesc = ExecTypeFromTL(lefttlist, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
- NULL,
+ parent->ps_ExprContext,
slot,
+ parent,
NULL);
- tupDesc = ExecTypeFromTL(rightptlist, false);
+ tupDesc = ExecTypeFromTL(righttlist, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projRight = ExecBuildProjectionInfo(righttlist,
sstate->innerecontext,
slot,
+ sstate->planstate,
NULL);
}
void
ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
{
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
SubLinkType subLinkType = subplan->subLinkType;
MemoryContext oldcontext;
ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
{
PlanState *planstate = node->planstate;
- SubPlan *subplan = (SubPlan *) node->xprstate.expr;
+ SubPlan *subplan = node->subplan;
EState *estate = parent->state;
ListCell *l;
SubPlan *subplan2;
Cost cost1;
Cost cost2;
+ ListCell *lc;
- asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
- asstate->xprstate.expr = (Expr *) asplan;
+ asstate->subplan = asplan;
/*
* Initialize subplans. (Can we get away with only initializing the one
* we're going to use?)
*/
- asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
- parent);
+ foreach(lc, asplan->subplans)
+ {
+ SubPlanState *sp = ExecInitSubPlan(lfirst(lc), parent);
+ asstate->subplans =
+ lappend(asstate->subplans, sp);
+ parent->subPlan = lappend(parent->subPlan, sp);
+ }
/*
* Select the one to be used. For this, we need an estimate of the number
* Note: in future we might consider changing to different subplans on the
* fly, in case the original rowcount estimate turns out to be way off.
*/
-static Datum
+Datum
ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull)
/*
* initialize child expressions
*/
- subquerystate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) subquerystate);
- subquerystate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) subquerystate);
+ subquerystate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
/*
* tuple table initialization
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual,
+ &scanstate->ss.ps);
/*
* tuple table initialization
scanstate->ns_names = tf->ns_names;
- scanstate->ns_uris = (List *)
- ExecInitExpr((Expr *) tf->ns_uris, (PlanState *) scanstate);
+ scanstate->ns_uris =
+ ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
scanstate->docexpr =
ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
scanstate->rowexpr =
ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
- scanstate->colexprs = (List *)
- ExecInitExpr((Expr *) tf->colexprs, (PlanState *) scanstate);
- scanstate->coldefexprs = (List *)
- ExecInitExpr((Expr *) tf->coldefexprs, (PlanState *) scanstate);
+ scanstate->colexprs =
+ ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
+ scanstate->coldefexprs =
+ ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
scanstate->notnulls = tf->notnulls;
static void
TidListCreate(TidScanState *tidstate)
{
- List *evalList = tidstate->tss_tidquals;
+ TidScan *node = (TidScan *) tidstate->ss.ps.plan;
+ List *evalList = node->tidquals;
ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
BlockNumber nblocks;
ItemPointerData *tidList;
foreach(l, evalList)
{
- ExprState *exstate = (ExprState *) lfirst(l);
- Expr *expr = exstate->expr;
+ Expr *expr = (Expr*) lfirst(l);
ItemPointer itemptr;
bool isNull;
if (is_opclause(expr))
{
- FuncExprState *fexstate = (FuncExprState *) exstate;
+ FuncExpr *fex = (FuncExpr *) expr;
+ ExprState *exprstate;
Node *arg1;
Node *arg2;
arg1 = get_leftop(expr);
arg2 = get_rightop(expr);
if (IsCTIDVar(arg1))
- exstate = (ExprState *) lsecond(fexstate->args);
+ exprstate = ExecInitExpr((Expr *) lsecond(fex->args),
+ &tidstate->ss.ps);
else if (IsCTIDVar(arg2))
- exstate = (ExprState *) linitial(fexstate->args);
- else
+ exprstate = ExecInitExpr((Expr *) linitial(fex->args),
+ &tidstate->ss.ps);
elog(ERROR, "could not identify CTID variable");
itemptr = (ItemPointer)
- DatumGetPointer(ExecEvalExprSwitchContext(exstate,
+ DatumGetPointer(ExecEvalExprSwitchContext(exprstate,
econtext,
&isNull));
if (!isNull &&
}
else if (expr && IsA(expr, ScalarArrayOpExpr))
{
- ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
+ ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
+ ExprState *exprstate;
Datum arraydatum;
ArrayType *itemarray;
Datum *ipdatums;
int ndatums;
int i;
- exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
- arraydatum = ExecEvalExprSwitchContext(exstate,
+ exprstate = ExecInitExpr(lsecond(saex->args), &tidstate->ss.ps);
+ arraydatum = ExecEvalExprSwitchContext(exprstate,
econtext,
&isNull);
if (isNull)
/*
* initialize child expressions
*/
- tidstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) tidstate);
- tidstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) tidstate);
-
- tidstate->tss_tidquals = (List *)
- ExecInitExpr((Expr *) node->tidquals,
- (PlanState *) tidstate);
+ tidstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
/*
* tuple table initialization
* is a SubPlan, and there shouldn't be any (any subselects in the
* VALUES list should be InitPlans).
*/
- exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
+ exprstatelist = ExecInitExprList(exprlist, NULL);
/* parser should have checked all sublists are the same length */
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* get info about values list
winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);
- winstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->plan.targetlist,
- (PlanState *) winstate);
-
/*
* WindowAgg nodes never have quals, since they can only occur at the
* logical top level of a query (ie, after any WHERE or HAVING filters)
*/
Assert(node->plan.qual == NIL);
- winstate->ss.ps.qual = NIL;
+ winstate->ss.ps.qual = NULL;
/*
* initialize child nodes
foreach(l, winstate->funcs)
{
WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
- WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
+ WindowFunc *wfunc = wfuncstate->wfunc;
WindowStatePerFunc perfuncstate;
AclResult aclresult;
int i;
/*
* initialize child expressions
*/
- scanstate->ss.ps.targetlist = (List *)
- ExecInitExpr((Expr *) node->scan.plan.targetlist,
- (PlanState *) scanstate);
- scanstate->ss.ps.qual = (List *)
- ExecInitExpr((Expr *) node->scan.plan.qual,
- (PlanState *) scanstate);
+ scanstate->ss.ps.qual =
+ ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization
* Else, make a scalar (argisrow == false) NullTest
* for this field. Scalar semantics are required
* because IS [NOT] NULL doesn't recurse; see comments
- * in ExecEvalNullTest().
+ * in ExecEvalRowNullInt().
*/
newntest = makeNode(NullTest);
newntest->arg = (Expr *) relem;
* FALSE: drop (does not affect result)
* TRUE: force result to TRUE
* NULL: keep only one
- * We must keep one NULL input because ExecEvalOr returns NULL when no input
- * is TRUE and at least one is NULL. We don't actually include the NULL
+ * We must keep one NULL input because OR expressions evaluate to NULL when no
+ * input is TRUE and at least one is NULL. We don't actually include the NULL
* here, that's supposed to be done by the caller.
*
* The output arguments *haveNull and *forceTrue must be initialized FALSE
* TRUE: drop (does not affect result)
* FALSE: force result to FALSE
* NULL: keep only one
- * We must keep one NULL input because ExecEvalAnd returns NULL when no input
- * is FALSE and at least one is NULL. We don't actually include the NULL
- * here, that's supposed to be done by the caller.
+ * We must keep one NULL input because AND expressions evaluate to NULL when
+ * no input is FALSE and at least one is NULL. We don't actually include the
+ * NULL here, that's supposed to be done by the caller.
*
* The output arguments *haveNull and *forceFalse must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or false constant,
/*
* domain_check_input - apply the cached checks.
*
- * This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
+ * This is similar to the handling of CoerceToDomain nodes in
+ * execExpr.c/execInterpExpr.c.
*/
static void
domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
/*
* Set up value to be returned by CoerceToDomainValue
- * nodes. Unlike ExecEvalCoerceToDomain, this econtext
- * couldn't be shared with anything else, so no need to
- * save and restore fields. But we do need to protect the
- * passed-in value against being changed by called
- * functions. (It couldn't be a R/W expanded object for
- * most uses, but that seems possible for domain_check().)
+ * nodes. Unlike in the generic expression case, this
+ * econtext couldn't be shared with anything else, so no
+ * need to save and restore fields. But we do need to
+ * protect the passed-in value against being changed by
+ * called functions. (It couldn't be a R/W expanded
+ * object for most uses, but that seems possible for
+ * domain_check().)
*/
econtext->domainValue_datum =
MakeExpandedObjectReadOnly(value, isnull,
my_extra->constraint_ref.tcache->typlen);
econtext->domainValue_isNull = isnull;
- conResult = ExecEvalExprSwitchContext(con->check_expr,
+ conResult = ExecEvalExprSwitchContext(con->check_exprstate,
econtext,
&conIsNull);
foreach(lc2, ps->subPlan)
{
SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
- SubPlan *subplan = (SubPlan *) sstate->xprstate.expr;
+ SubPlan *subplan = sstate->subplan;
ListCell *lc3;
ListCell *lc4;
continue;
/* No parameters to be had here. */
- Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+ Assert(sstate->subplan->parParam == NIL);
/* Keep looking, but we are emerging from an initplan. */
in_same_plan_level = false;
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
-#include "executor/executor.h"
#include "executor/spi.h"
#include "executor/tablefunc.h"
#include "fmgr.h"
xmltype *
-xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
+xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue)
{
#ifdef USE_LIBXML
- XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
xmltype *result;
List *named_arg_strings;
List *arg_strings;
volatile xmlTextWriterPtr writer = NULL;
/*
- * We first evaluate all the arguments, then start up libxml and create
- * the result. This avoids issues if one of the arguments involves a call
- * to some other function or subsystem that wants to use libxml on its own
- * terms.
+ * All arguments are already evaluated. This avoids issues if one of the
+ * arguments involves a call to some other function or subsystem that
+ * wants to use libxml on its own terms.
*/
named_arg_strings = NIL;
i = 0;
- foreach(arg, xmlExpr->named_args)
+ foreach(arg, xexpr->named_args)
{
- ExprState *e = (ExprState *) lfirst(arg);
- Datum value;
- bool isnull;
+ Expr *e = (Expr *) lfirst(arg);
char *str;
- value = ExecEvalExpr(e, econtext, &isnull);
- if (isnull)
+ if (named_argnull[i])
str = NULL;
else
- str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
+ str = map_sql_value_to_xml_value(named_argvalue[i], exprType((Node *) e), false);
named_arg_strings = lappend(named_arg_strings, str);
i++;
}
arg_strings = NIL;
- foreach(arg, xmlExpr->args)
+ i = 0;
+ foreach(arg, xexpr->args)
{
- ExprState *e = (ExprState *) lfirst(arg);
- Datum value;
- bool isnull;
+ Expr *e = (Expr *) lfirst(arg);
char *str;
- value = ExecEvalExpr(e, econtext, &isnull);
/* here we can just forget NULL elements immediately */
- if (!isnull)
+ if (!argnull[i])
{
- str = map_sql_value_to_xml_value(value,
- exprType((Node *) e->expr), true);
+ str = map_sql_value_to_xml_value(argvalue[i],
+ exprType((Node *) e), true);
arg_strings = lappend(arg_strings, str);
}
+ i++;
}
/* now safe to run libxml */
* the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
* Such a state tree is not part of the DomainConstraintCache, but is
* considered to belong to a DomainConstraintRef.
+ * FIXME: update.
*/
struct DomainConstraintCache
{
r = makeNode(DomainConstraintState);
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
- /* Must cast here because we're not storing an expr state node */
- r->check_expr = (ExprState *) check_expr;
+ r->check_expr = check_expr;
+ r->check_exprstate = NULL;
MemoryContextSwitchTo(oldcxt);
r->constrainttype = DOM_CONSTRAINT_NOTNULL;
r->name = pstrdup("NOT NULL");
r->check_expr = NULL;
+ r->check_exprstate = NULL;
/* lcons to apply the nullness check FIRST */
dcc->constraints = lcons(r, dcc->constraints);
newr = makeNode(DomainConstraintState);
newr->constrainttype = r->constrainttype;
newr->name = r->name;
- /* Must cast here because cache items contain expr plan trees */
- newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
+ newr->check_expr = r->check_expr;
+ newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
result = lappend(result, newr);
}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.h
+ * Low level infrastructure related to expression evaluation
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * src/include/executor/execExpr.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef EXEC_EXPR_H
+#define EXEC_EXPR_H
+
+#include "nodes/execnodes.h"
+
+struct ArrayRefState;
+
+#define EEO_FLAG_JUMP_THREADED (1 << 0)
+
+typedef enum ExprEvalOp
+{
+ EEO_DONE,
+ EEO_INNER_FETCHSOME,
+ EEO_OUTER_FETCHSOME,
+ EEO_SCAN_FETCHSOME,
+ EEO_INNER_VAR,
+ EEO_OUTER_VAR,
+ EEO_SCAN_VAR,
+ EEO_ASSIGN_INNER_VAR,
+ EEO_ASSIGN_OUTER_VAR,
+ EEO_ASSIGN_SCAN_VAR,
+ EEO_ASSIGN_TMP,
+ EEO_ASSIGN_TMP_UNEXPAND,
+ EEO_INNER_SYSVAR,
+ EEO_OUTER_SYSVAR,
+ EEO_SCAN_SYSVAR,
+ EEO_CONST,
+ EEO_FUNCEXPR,
+ EEO_FUNCEXPR_STRICT,
+ EEO_FUNCEXPR_FUSAGE,
+ EEO_FUNCEXPR_STRICT_FUSAGE,
+ EEO_BOOL_AND_STEP_FIRST,
+ EEO_BOOL_AND_STEP,
+ EEO_BOOL_AND_STEP_LAST,
+ EEO_BOOL_OR_STEP_FIRST,
+ EEO_BOOL_OR_STEP,
+ EEO_BOOL_OR_STEP_LAST,
+ EEO_BOOL_NOT_STEP,
+ EEO_QUAL,
+ EEO_NULLTEST_ISNULL,
+ EEO_NULLTEST_ISNOTNULL,
+ EEO_NULLTEST_ROWISNULL,
+ EEO_NULLTEST_ROWISNOTNULL,
+ EEO_PARAM_EXEC,
+ EEO_PARAM_EXTERN,
+ EEO_CASE_WHEN_STEP,
+ EEO_CASE_THEN_STEP,
+ EEO_CASE_TESTVAL,
+ EEO_CASE_TESTVAL_UNEXPAND,
+ EEO_COALESCE,
+ EEO_BOOLTEST_IS_TRUE,
+ EEO_BOOLTEST_IS_NOT_TRUE,
+ EEO_BOOLTEST_IS_FALSE,
+ EEO_BOOLTEST_IS_NOT_FALSE,
+ EEO_BOOLTEST_IS_UNKNOWN,
+ EEO_BOOLTEST_IS_NOT_UNKNOWN,
+ EEO_WHOLEROW,
+ EEO_IOCOERCE,
+ EEO_DISTINCT,
+ EEO_NULLIF,
+ EEO_SQLVALUEFUNCTION,
+ EEO_CURRENTOFEXPR,
+ EEO_ARRAYEXPR,
+ EEO_ARRAYCOERCE,
+ EEO_ROW,
+ EEO_ROWCOMPARE_STEP,
+ EEO_ROWCOMPARE_FINAL,
+ EEO_MINMAX,
+ EEO_FIELDSELECT,
+ EEO_FIELDSTORE_DEFORM,
+ EEO_FIELDSTORE_FORM,
+ EEO_ARRAYREF_CHECKINPUT,
+ EEO_ARRAYREF_CHECKSUBSCRIPT,
+ EEO_ARRAYREF_OLD,
+ EEO_ARRAYREF_ASSIGN,
+ EEO_ARRAYREF_FETCH,
+ EEO_CONVERT_ROWTYPE,
+ EEO_SCALARARRAYOP,
+ EEO_DOMAIN_TESTVAL,
+ EEO_DOMAIN_TESTVAL_UNEXPAND,
+ EEO_DOMAIN_NOTNULL,
+ EEO_DOMAIN_CHECK,
+ EEO_XMLEXPR,
+ EEO_AGGREF,
+ EEO_GROUPING_FUNC,
+ EEO_WINDOW_FUNC,
+ EEO_SUBPLAN,
+ EEO_ALTERNATIVE_SUBPLAN,
+ EEO_LAST
+} ExprEvalOp;
+
+
+typedef struct ExprEvalStep
+{
+ /*
+ * Instruction to be executed. During instruction preparation this is an
+ * ExprEvalOp, but during execution it can be swapped out to some other
+ * type, e.g. a pointer for computed goto (that's why it's a size_t).
+ */
+ size_t opcode;
+
+ /* target for the result of the current instruction */
+ bool *resnull;
+ Datum *resvalue;
+
+ /*
+ * Data for an operation. Inline stored data is faster to access, but also
+ * bloats the size of all instructions. The union should be kept below 48
+ * bytes (so the entire struct is below 64bytes, a single cacheline on
+ * common systems).
+ */
+ union
+ {
+ struct
+ {
+ int attnum;
+ } var;
+
+ struct
+ {
+ Var *var;
+ bool first; /* first time through, initialize */
+ TupleDesc tupdesc; /* descriptor for resulting tuples */
+ JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
+ } wholerow;
+
+ struct
+ {
+ Datum value;
+ bool isnull;
+ } constval;
+
+ struct
+ {
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo_data;
+ /* faster to access without additional indirection */
+ PGFunction fn_addr;
+ int nargs;
+ } func;
+
+ struct
+ {
+ Datum *value;
+ bool *isnull;
+ bool *anynull;
+ int jumpdone;
+ } boolexpr;
+
+ struct
+ {
+ int jumpdone;
+ } qualexpr;
+
+ struct
+ {
+ TupleDesc argdesc;
+ } nulltest_row;
+
+ struct
+ {
+ int paramid;
+ int paramtype;
+ } param;
+
+ struct
+ {
+ Datum *value;
+ bool *isnull;
+ int jumpfalse;
+ } casewhen;
+
+ struct
+ {
+ Datum *value;
+ bool *isnull;
+ int jumpdone;
+ } casethen;
+
+ struct
+ {
+ Datum *value;
+ bool *isnull;
+ } casetest;
+
+ struct
+ {
+ int jumpdone;
+ } coalesce;
+
+ struct
+ {
+ /* lookup info for source output function */
+ FmgrInfo *finfo_out;
+ FunctionCallInfo fcinfo_data_out;
+ /* lookup info for result input function */
+ FmgrInfo *finfo_in;
+ FunctionCallInfo fcinfo_data_in;
+ Oid intypioparam; /* argument needed for input function */
+ } iocoerce;
+
+ struct
+ {
+ SQLValueFunction *svf;
+ } sqlvaluefunction;
+
+ struct
+ {
+ ArrayExpr *arrayexpr;
+ Datum *elemvalues;
+ bool *elemnulls;
+ int nelems;
+ int16 elemlength; /* typlen of the array element type */
+ bool elembyval; /* is the element type pass-by-value? */
+ char elemalign; /* typalign of the element type */
+ } arrayexpr;
+
+ struct
+ {
+ ArrayCoerceExpr *coerceexpr;
+ Oid resultelemtype; /* element type of result array */
+ FmgrInfo *elemfunc; /* lookup info for element coercion
+ * function */
+ struct ArrayMapState *amstate; /* workspace for array_map */
+ } arraycoerce;
+
+ struct
+ {
+ RowExpr *rowexpr;
+ /* the arguments */
+ Datum *elemvalues;
+ bool *elemnulls;
+ TupleDesc tupdesc; /* descriptor for result tuples */
+ } row;
+
+ struct
+ {
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo_data;
+ PGFunction fn_addr;
+ int jumpnull;
+ int jumpdone;
+ } rowcompare_step;
+
+ struct
+ {
+ RowCompareType rctype;
+ } rowcompare_final;
+
+ struct
+ {
+ /* the arguments */
+ Datum *values;
+ bool *nulls;
+ int nelems;
+
+ MinMaxOp op;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo_data;
+ } minmax;
+
+ struct
+ {
+ /* tupdesc for most recent input */
+ TupleDesc argdesc;
+ AttrNumber fieldnum;
+ Oid resulttype;
+ } fieldselect;
+
+ struct
+ {
+ /* tupdesc for most recent input */
+ TupleDesc *argdesc;
+ FieldStore *fstore;
+
+ /* the arguments */
+ Datum *values;
+ bool *nulls;
+ } fieldstore;
+
+ struct
+ {
+ /* too big to have inline */
+ struct ArrayRefState *state;
+ int jumpdone;
+ } arrayref;
+
+ struct
+ {
+ /* too big to have inline */
+ struct ArrayRefState *state;
+ int off;
+ int jumpdone;
+ bool isupper;
+ } arrayref_checksubscript;
+
+ struct
+ {
+ ConvertRowtypeExpr *convert;
+ TupleDesc indesc;
+ TupleDesc outdesc;
+ TupleConversionMap *map;
+ bool initialized;
+ } convert_rowtype;
+
+ struct
+ {
+ ScalarArrayOpExpr *opexpr;
+ Oid element_type;
+ int16 typlen;
+ bool typbyval;
+ char typalign;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo_data;
+ PGFunction fn_addr;
+ } scalararrayop;
+
+ struct
+ {
+ char *constraintname;
+ Datum *checkvalue;
+ bool *checknull;
+ Oid resulttype;
+ } domaincheck;
+
+ struct
+ {
+ XmlExpr *xexpr;
+ Datum *named_argvalue;
+ bool *named_argnull;
+ Datum *argvalue;
+ bool *argnull;
+ } xmlexpr;
+
+ struct
+ {
+ AggrefExprState *astate;
+ } aggref;
+
+ struct
+ {
+ AggState *parent;
+ List *clauses;
+ } grouping_func;
+
+ struct
+ {
+ WindowFuncExprState *wfstate;
+ } window_func;
+
+ struct
+ {
+ SubPlanState *sstate;
+ } subplan;
+
+ struct
+ {
+ AlternativeSubPlanState *asstate;
+ } alternative_subplan;
+
+ struct
+ {
+ size_t resultnum;
+ int attnum;
+ } assign_var;
+
+ struct
+ {
+ size_t resultnum;
+ } assign_tmp;
+
+ struct
+ {
+ int last_var;
+ } fetch;
+ } d;
+} ExprEvalStep;
+
+
+typedef struct ArrayRefState
+{
+ bool isassignment;
+ int numupper;
+ Datum upper[MAXDIM];
+ int upperindex[MAXDIM];
+ bool uppernull[MAXDIM];
+ bool upperprovided[MAXDIM];
+ int numlower;
+ Datum lower[MAXDIM];
+ int lowerindex[MAXDIM];
+ bool lowernull[MAXDIM];
+ bool lowerprovided[MAXDIM];
+
+ Oid refelemtype;
+ int16 refattrlength; /* typlen of array type */
+ int16 refelemlength; /* typlen of the array element type */
+ bool refelembyval; /* is the element type pass-by-value? */
+ char refelemalign; /* typalign of the element type */
+
+ Datum replacevalue;
+ bool replacenull;
+
+ Datum prevvalue;
+ bool prevnull;
+} ArrayRefState;
+
+extern void ExecInstantiateInterpretedExpr(ExprState *state);
+
+extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
+
+/*
+ * Non fast-path execution functions. These are externs instead of static in
+ * execInterpExpr.c, because that allows them to be used by other methods of
+ * expression evaluation.
+ */
+extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern bool ExecEvalArrayRefCheckSubscript(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
+extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op, ExprContext *econtext);
+
+#endif /* EXEC_EXPR_H */
#undef EXEC_NESTLOOPDEBUG
*/
-/* ----------------
- * EXEC_EVALDEBUG is a flag which turns on debugging of
- * ExecEval and ExecTargetList() stuff by EV_printf() in execQual.c
- * ----------------
-#undef EXEC_EVALDEBUG
- */
-
/* ----------------
* EXEC_SORTDEBUG is a flag which turns on debugging of
* the ExecSort() stuff by SO_printf() in nodeSort.c
#define ENL1_printf(message)
#endif /* EXEC_NESTLOOPDEBUG */
-/* ----------------
- * exec eval / target list debugging defines
- * ----------------
- */
-#ifdef EXEC_EVALDEBUG
-#define EV_nodeDisplay(l) nodeDisplay(l)
-#define EV_printf(s) printf(s)
-#define EV1_printf(s, a) printf(s, a)
-#else
-#define EV_nodeDisplay(l)
-#define EV_printf(s)
-#define EV1_printf(s, a)
-#endif /* EXEC_EVALDEBUG */
-
/* ----------------
* sort node debugging defines
* ----------------
* now it's just a macro invoking the function pointed to by an ExprState
* node. Beware of double evaluation of the ExprState argument!
*/
-#define ExecEvalExpr(expr, econtext, isNull) \
- ((*(expr)->evalfunc) (expr, econtext, isNull))
+#define ExecEvalExpr(state, econtext, isNull) \
+ (state)->evalfunc((state), econtext, isNull)
/* Hook for plugins to get control in ExecutorStart() */
bool *isNull);
extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
bool *isNull);
-extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
+extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
ExprContext *econtext,
MemoryContext argContext,
TupleDesc expectedDesc,
bool randomAccess);
-extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
+extern Datum ExecMakeFunctionResultSet(SetExprState *setexpr,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone);
-extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
- bool *isNull);
+extern SetExprState *ExecInitFunctionResultSet(Expr *expr, ExprContext *econtext, PlanState *parent);
+extern SetExprState *ExecInitTableFunctionResult(Expr *expr, ExprContext *econtext, PlanState *parent);
+
+/*
+ * prototypes from functions in execExpr.c
+ */
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
+extern List *ExecInitExprList(List *nodes, PlanState *parent);
extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
-extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
+extern List *ExecPrepareExprList(List *nodes, EState *estate);
+extern ExprState *ExecPrepareQual(List *qual, EState *estate);
+extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
+extern ExprState *ExecInitQual(List *qual, PlanState *parent);
+extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
+
+extern bool ExecCheck(ExprState *state, ExprContext *context);
extern int ExecTargetListLength(List *targetlist);
extern int ExecCleanTargetListLength(List *targetlist);
-extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
+
+/*
+ * ExecEvalExprSwitchContext
+ *
+ * Same as ExecEvalExpr, but get into the right allocation context explicitly.
+ */
+#ifndef FRONTEND
+static inline Datum
+ExecEvalExprSwitchContext(ExprState *state,
+ ExprContext *econtext,
+ bool *isNull)
+{
+ Datum retDatum;
+ MemoryContext oldContext;
+
+ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
+ retDatum = ExecEvalExpr(state, econtext, isNull);
+ MemoryContextSwitchTo(oldContext);
+ return retDatum;
+}
+#endif
+
+/*
+ * ExecProject
+ *
+ * Projects a tuple based on projection info and stores it in the slot passed
+ * to ExecBuildProjectInfo().
+ *
+ * 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.
+ */
+#ifndef FRONTEND
+static inline TupleTableSlot *
+ExecProject(ProjectionInfo *projInfo)
+{
+ bool isnull;
+ ExprContext *econtext = projInfo->pi_exprContext;
+ ExprState *state = &projInfo->pi_state;
+ TupleTableSlot *slot = state->resultslot;
+
+ /*
+ * 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);
+
+ ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+ /*
+ * Successfully formed a result row. Mark the result slot as containing a
+ * valid virtual tuple.
+ */
+ slot->tts_isempty = false;
+ slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
+
+ return slot;
+}
+#endif
+
+/*
+ * ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
+ * ExecPrepareQual).
+ */
+#ifndef FRONTEND
+static inline bool
+ExecQual(ExprState *state, ExprContext *econtext)
+{
+ bool isnull;
+ Datum ret;
+
+ /* short-circuit (here and in ExecInitQual) for empty restriction list */
+ if (state == NULL)
+ return true;
+
+ ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+ /* EEO_QUAL should never return NULL */
+ Assert(!isnull);
+
+ return DatumGetBool(ret);
+}
+#endif
/*
* prototypes from functions in execScan.c
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot,
+ PlanState *planstate,
TupleDesc inputDesc);
extern void ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc);
extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
+extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
+extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
+
extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);
#include "storage/condition_variable.h"
-/* ----------------
- * IndexInfo information
- *
- * this struct holds the information needed to construct new index
- * entries for a particular index. Used for both index_build and
- * retail creation of index entries.
- *
- * NumIndexAttrs number of columns in this index
- * KeyAttrNumbers underlying-rel attribute numbers used as keys
- * (zeroes indicate expressions)
- * Expressions expr trees for expression entries, or NIL if none
- * ExpressionsState exec state for expressions, or NIL if none
- * Predicate partial-index predicate, or NIL if none
- * PredicateState exec state for predicate, or NIL if none
- * ExclusionOps Per-column exclusion operators, or NULL if none
- * ExclusionProcs Underlying function OIDs for ExclusionOps
- * ExclusionStrats Opclass strategy numbers for ExclusionOps
- * UniqueOps Theses are like Exclusion*, but for unique indexes
- * UniqueProcs
- * UniqueStrats
- * Unique is it a unique index?
- * ReadyForInserts is it valid for inserts?
- * Concurrent are we doing a concurrent index build?
- * BrokenHotChain did we detect any broken HOT chains?
- * AmCache private cache area for index AM
- * Context memory context holding this IndexInfo
- *
- * ii_Concurrent and ii_BrokenHotChain are used only during index build;
- * they're conventionally set to false otherwise.
- * ----------------
- */
-typedef struct IndexInfo
-{
- NodeTag type;
- int ii_NumIndexAttrs;
- AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS];
- List *ii_Expressions; /* list of Expr */
- List *ii_ExpressionsState; /* list of ExprState */
- List *ii_Predicate; /* list of Expr */
- List *ii_PredicateState; /* list of ExprState */
- Oid *ii_ExclusionOps; /* array with one entry per column */
- Oid *ii_ExclusionProcs; /* array with one entry per column */
- uint16 *ii_ExclusionStrats; /* array with one entry per column */
- Oid *ii_UniqueOps; /* array with one entry per column */
- Oid *ii_UniqueProcs; /* array with one entry per column */
- uint16 *ii_UniqueStrats; /* array with one entry per column */
- bool ii_Unique;
- bool ii_ReadyForInserts;
- bool ii_Concurrent;
- bool ii_BrokenHotChain;
- void *ii_AmCache;
- MemoryContext ii_Context;
-} IndexInfo;
-
/* ----------------
* ExprContext_CB
*
TupleDesc setDesc; /* actual descriptor for returned tuples */
} ReturnSetInfo;
+/* ----------------
+ * ExprState node
+ *
+ * ExprState is the top-level node for expression evaluation. It contains
+ * instructions (in ->steps) to evaluate the expression.
+ * ----------------
+ */
+typedef struct ExprState ExprState;
+
+typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
+ ExprContext *econtext,
+ bool *isNull);
+struct ExprState
+{
+ Node tag;
+
+ bool resnull;
+ int8 flags;
+ Datum resvalue;
+
+ /* forward declaration, to avoid including execExpr.h everywhere */
+ struct ExprEvalStep *steps;
+
+ TupleTableSlot *resultslot; /* slot for projections */
+
+ /* evaluate expression */
+ ExprStateEvalFunc evalfunc;
+
+ /* original expression, for debugging only */
+ Expr *expr;
+
+ /*
+ * XXX: following only needed during "compilation", could be thrown away.
+ */
+ size_t steps_len;
+ size_t steps_alloc;
+
+ Datum *innermost_caseval;
+ bool *innermost_casenull;
+
+ Datum *innermost_domainval;
+ bool *innermost_domainnull;
+};
+
/* ----------------
* ProjectionInfo node information
*
* that is, form new tuples by evaluation of targetlist expressions.
* Nodes which need to do projections create one of these.
*
- * ExecProject() evaluates the tlist, forms a tuple, and stores it
- * in the given slot. Note that the result will be a "virtual" tuple
- * unless ExecMaterializeSlot() is then called to force it to be
- * converted to a physical tuple. The slot must have a tupledesc
- * that matches the output of the tlist!
- *
- * The planner very often produces tlists that consist entirely of
- * simple Var references (lower levels of a plan tree almost always
- * look like that). And top-level tlists are often mostly Vars too.
- * We therefore optimize execution of simple-Var tlist entries.
- * The pi_targetlist list actually contains only the tlist entries that
- * aren't simple Vars, while those that are Vars are processed using the
- * varSlotOffsets/varNumbers/varOutputCols arrays.
- *
- * The lastXXXVar fields are used to optimize fetching of fields from
- * input tuples: they let us do a slot_getsomeattrs() call to ensure
- * that all needed attributes are extracted in one pass.
- *
- * targetlist target list for projection (non-Var expressions only)
+ * pi_state instructions to evaluate projection
* exprContext expression context in which to evaluate targetlist
* slot slot to place projection result in
- * directMap true if varOutputCols[] is an identity map
- * numSimpleVars number of simple Vars found in original tlist
- * varSlotOffsets array indicating which slot each simple Var is from
- * varNumbers array containing input attr numbers of simple Vars
- * varOutputCols array containing output attr numbers of simple Vars
- * lastInnerVar highest attnum from inner tuple slot (0 if none)
- * lastOuterVar highest attnum from outer tuple slot (0 if none)
- * lastScanVar highest attnum from scan tuple slot (0 if none)
* ----------------
*/
typedef struct ProjectionInfo
{
NodeTag type;
- List *pi_targetlist;
+ ExprState pi_state;
ExprContext *pi_exprContext;
- TupleTableSlot *pi_slot;
- bool pi_directMap;
- int pi_numSimpleVars;
- int *pi_varSlotOffsets;
- int *pi_varNumbers;
- int *pi_varOutputCols;
- int pi_lastInnerVar;
- int pi_lastOuterVar;
- int pi_lastScanVar;
} ProjectionInfo;
/* ----------------
AttrNumber jf_junkAttNo;
} JunkFilter;
+/* ----------------
+ * IndexInfo information
+ *
+ * this struct holds the information needed to construct new index
+ * entries for a particular index. Used for both index_build and
+ * retail creation of index entries.
+ *
+ * NumIndexAttrs number of columns in this index
+ * KeyAttrNumbers underlying-rel attribute numbers used as keys
+ * (zeroes indicate expressions)
+ * Expressions expr trees for expression entries, or NIL if none
+ * ExpressionsState exec state for expressions, or NIL if none
+ * Predicate partial-index predicate, or NIL if none
+ * PredicateState exec state for predicate, or NIL if none
+ * ExclusionOps Per-column exclusion operators, or NULL if none
+ * ExclusionProcs Underlying function OIDs for ExclusionOps
+ * ExclusionStrats Opclass strategy numbers for ExclusionOps
+ * UniqueOps Theses are like Exclusion*, but for unique indexes
+ * UniqueProcs
+ * UniqueStrats
+ * Unique is it a unique index?
+ * ReadyForInserts is it valid for inserts?
+ * Concurrent are we doing a concurrent index build?
+ * BrokenHotChain did we detect any broken HOT chains?
+ * AmCache private cache area for index AM
+ * Context memory context holding this IndexInfo
+ *
+ * ii_Concurrent and ii_BrokenHotChain are used only during index build;
+ * they're conventionally set to false otherwise.
+ * ----------------
+ */
+typedef struct IndexInfo
+{
+ NodeTag type;
+ int ii_NumIndexAttrs;
+ AttrNumber ii_KeyAttrNumbers[INDEX_MAX_KEYS];
+ List *ii_Expressions; /* list of Expr */
+ List *ii_ExpressionsState; /* list of ExprState */
+ List *ii_Predicate; /* list of Expr */
+ ExprState *ii_PredicateState;
+ Oid *ii_ExclusionOps; /* array with one entry per column */
+ Oid *ii_ExclusionProcs; /* array with one entry per column */
+ uint16 *ii_ExclusionStrats; /* array with one entry per column */
+ Oid *ii_UniqueOps; /* array with one entry per column */
+ Oid *ii_UniqueProcs; /* array with one entry per column */
+ uint16 *ii_UniqueStrats; /* array with one entry per column */
+ bool ii_Unique;
+ bool ii_ReadyForInserts;
+ bool ii_Concurrent;
+ bool ii_BrokenHotChain;
+ void *ii_AmCache;
+ MemoryContext ii_Context;
+} IndexInfo;
+
/* ----------------
* ResultRelInfo information
*
IndexInfo **ri_IndexRelationInfo;
TriggerDesc *ri_TrigDesc;
FmgrInfo *ri_TrigFunctions;
- List **ri_TrigWhenExprs;
+ ExprState **ri_TrigWhenExprs;
Instrumentation *ri_TrigInstrument;
struct FdwRoutine *ri_FdwRoutine;
void *ri_FdwState;
bool ri_usesFdwDirectModify;
List *ri_WithCheckOptions;
List *ri_WithCheckOptionExprs;
- List **ri_ConstraintExprs;
+ ExprState **ri_ConstraintExprs;
JunkFilter *ri_junkFilter;
ProjectionInfo *ri_projectReturning;
ProjectionInfo *ri_onConflictSetProj;
- List *ri_onConflictSetWhere;
+ ExprState *ri_onConflictSetWhere;
List *ri_PartitionCheck;
- List *ri_PartitionCheckExpr;
+ ExprState *ri_PartitionCheckExpr;
Relation ri_PartitionRoot;
} ResultRelInfo;
bool *es_epqScanDone; /* true if EPQ tuple has been fetched */
/* The per-query shared memory area to use for parallel execution. */
- struct dsa_area *es_query_dsa;
+ struct dsa_area *es_query_dsa;
} EState;
#define ScanTupleHashTable(htable, iter) \
tuplehash_iterate(htable->hashtab, iter)
-
-/* ----------------------------------------------------------------
- * Expression State Trees
- *
- * Each executable expression tree has a parallel ExprState tree.
- *
- * Unlike PlanState, there is not an exact one-for-one correspondence between
- * ExprState node types and Expr node types. Many Expr node types have no
- * need for node-type-specific run-time state, and so they can use plain
- * ExprState or GenericExprState as their associated ExprState node type.
- * ----------------------------------------------------------------
- */
-
-/* ----------------
- * ExprState node
- *
- * ExprState is the common superclass for all ExprState-type nodes.
- *
- * It can also be instantiated directly for leaf Expr nodes that need no
- * local run-time state (such as Var, Const, or Param).
- *
- * To save on dispatch overhead, each ExprState node contains a function
- * pointer to the routine to execute to evaluate the node.
- * ----------------
- */
-
-typedef struct ExprState ExprState;
-
-typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
- ExprContext *econtext,
- bool *isNull);
-
-struct ExprState
-{
- NodeTag type;
- Expr *expr; /* associated Expr node */
- ExprStateEvalFunc evalfunc; /* routine to run to execute node */
-};
-
-/* ----------------
- * GenericExprState node
- *
- * This is used for Expr node types that need no local run-time state,
- * but have one child Expr node.
- * ----------------
- */
-typedef struct GenericExprState
-{
- ExprState xprstate;
- ExprState *arg; /* state of my child node */
-} GenericExprState;
-
-/* ----------------
- * WholeRowVarExprState node
- * ----------------
- */
-typedef struct WholeRowVarExprState
-{
- ExprState xprstate;
- struct PlanState *parent; /* parent PlanState, or NULL if none */
- TupleDesc wrv_tupdesc; /* descriptor for resulting tuples */
- JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
-} WholeRowVarExprState;
-
/* ----------------
* AggrefExprState node
* ----------------
*/
typedef struct AggrefExprState
{
- ExprState xprstate;
+ NodeTag type;
+ Aggref *aggref;
int aggno; /* ID number for agg within its plan node */
} AggrefExprState;
-/* ----------------
- * GroupingFuncExprState node
- *
- * The list of column numbers refers to the input tuples of the Agg node to
- * which the GroupingFunc belongs, and may contain 0 for references to columns
- * that are only present in grouping sets processed by different Agg nodes (and
- * which are therefore always considered "grouping" here).
- * ----------------
- */
-typedef struct GroupingFuncExprState
-{
- ExprState xprstate;
- struct AggState *aggstate;
- List *clauses; /* integer list of column numbers */
-} GroupingFuncExprState;
-
/* ----------------
* WindowFuncExprState node
* ----------------
*/
typedef struct WindowFuncExprState
{
- ExprState xprstate;
+ NodeTag type;
+ WindowFunc *wfunc;
List *args; /* states of argument expressions */
ExprState *aggfilter; /* FILTER expression */
int wfuncno; /* ID number for wfunc within its plan node */
} WindowFuncExprState;
-/* ----------------
- * ArrayRefExprState node
- *
- * Note: array types can be fixed-length (typlen > 0), but only when the
- * element type is itself fixed-length. Otherwise they are varlena structures
- * and have typlen = -1. In any case, an array type is never pass-by-value.
- * ----------------
- */
-typedef struct ArrayRefExprState
-{
- ExprState xprstate;
- List *refupperindexpr; /* states for child nodes */
- List *reflowerindexpr;
- ExprState *refexpr;
- ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
- bool refelembyval; /* is the element type pass-by-value? */
- char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
/* ----------------
- * FuncExprState node
+ * SetExprState node
*
- * Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
- * and NullIf nodes; be careful to check what xprstate.expr is actually
- * pointing at!
+ * State for evaluating a potentially set returning expression (like FuncExpr
+ * or OpExpr). In some cases, like some of the expressions in ROWS FROM(...)
+ * the expression might not be a SRF, but uses the same machinery as SRFs
+ * (i.e. are treated like a SRF returning a single row).
* ----------------
*/
-typedef struct FuncExprState
+typedef struct SetExprState
{
- ExprState xprstate;
+ NodeTag type;
+ Expr *expr;
List *args; /* states of argument expressions */
+ /*
+ * In ROWS FROM functions possibly can be inlined removing the FuncExpr
+ * normally inside, this is the containing expression (which cannot return
+ * a set) which'll be evaluated using the normal ExecEvalExpr().
+ */
+ ExprState *elidedFuncState;
+
/*
* Function manager's lookup info for the target function. If func.fn_oid
* is InvalidOid, we haven't initialized it yet (nor any of the following
* argument values between calls, when setArgsValid is true.
*/
FunctionCallInfoData fcinfo_data;
-} FuncExprState;
-
-/* ----------------
- * ScalarArrayOpExprState node
- *
- * This is a FuncExprState plus some additional data.
- * ----------------
- */
-typedef struct ScalarArrayOpExprState
-{
- FuncExprState fxprstate;
- /* Cached info about array element type */
- Oid element_type;
- int16 typlen;
- bool typbyval;
- char typalign;
-} ScalarArrayOpExprState;
-
-/* ----------------
- * BoolExprState node
- * ----------------
- */
-typedef struct BoolExprState
-{
- ExprState xprstate;
- List *args; /* states of argument expression(s) */
-} BoolExprState;
+} SetExprState;
/* ----------------
* SubPlanState node
*/
typedef struct SubPlanState
{
- ExprState xprstate;
+ NodeTag type;
+ SubPlan *subplan;
struct PlanState *planstate; /* subselect plan's state tree */
struct PlanState *parent; /* parent plan node's state tree */
ExprState *testexpr; /* state of combining expression */
*/
typedef struct AlternativeSubPlanState
{
- ExprState xprstate;
+ NodeTag type;
+ AlternativeSubPlan *subplan;
List *subplans; /* states of alternative subplans */
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
-/* ----------------
- * FieldSelectState node
- * ----------------
- */
-typedef struct FieldSelectState
-{
- ExprState xprstate;
- ExprState *arg; /* input expression */
- TupleDesc argdesc; /* tupdesc for most recent input */
-} FieldSelectState;
-
-/* ----------------
- * FieldStoreState node
- * ----------------
- */
-typedef struct FieldStoreState
-{
- ExprState xprstate;
- ExprState *arg; /* input tuple value */
- List *newvals; /* new value(s) for field(s) */
- TupleDesc argdesc; /* tupdesc for most recent input */
-} FieldStoreState;
-
-/* ----------------
- * CoerceViaIOState node
- * ----------------
- */
-typedef struct CoerceViaIOState
-{
- ExprState xprstate;
- ExprState *arg; /* input expression */
- FmgrInfo outfunc; /* lookup info for source output function */
- FmgrInfo infunc; /* lookup info for result input function */
- Oid intypioparam; /* argument needed for input function */
-} CoerceViaIOState;
-
-/* ----------------
- * ArrayCoerceExprState node
- * ----------------
- */
-typedef struct ArrayCoerceExprState
-{
- ExprState xprstate;
- ExprState *arg; /* input array value */
- Oid resultelemtype; /* element type of result array */
- FmgrInfo elemfunc; /* lookup info for element coercion function */
- /* use struct pointer to avoid including array.h here */
- struct ArrayMapState *amstate; /* workspace for array_map */
-} ArrayCoerceExprState;
-
-/* ----------------
- * ConvertRowtypeExprState node
- * ----------------
- */
-typedef struct ConvertRowtypeExprState
-{
- ExprState xprstate;
- ExprState *arg; /* input tuple value */
- TupleDesc indesc; /* tupdesc for source rowtype */
- TupleDesc outdesc; /* tupdesc for result rowtype */
- /* use "struct" so we needn't include tupconvert.h here */
- struct TupleConversionMap *map;
- bool initialized;
-} ConvertRowtypeExprState;
-
-/* ----------------
- * CaseExprState node
- * ----------------
- */
-typedef struct CaseExprState
-{
- ExprState xprstate;
- ExprState *arg; /* implicit equality comparison argument */
- List *args; /* the arguments (list of WHEN clauses) */
- ExprState *defresult; /* the default result (ELSE clause) */
- int16 argtyplen; /* if arg is provided, its typlen */
-} CaseExprState;
-
-/* ----------------
- * CaseWhenState node
- * ----------------
- */
-typedef struct CaseWhenState
-{
- ExprState xprstate;
- ExprState *expr; /* condition expression */
- ExprState *result; /* substitution result */
-} CaseWhenState;
-
-/* ----------------
- * ArrayExprState node
- *
- * Note: ARRAY[] expressions always produce varlena arrays, never fixed-length
- * arrays.
- * ----------------
- */
-typedef struct ArrayExprState
-{
- ExprState xprstate;
- List *elements; /* states for child nodes */
- int16 elemlength; /* typlen of the array element type */
- bool elembyval; /* is the element type pass-by-value? */
- char elemalign; /* typalign of the element type */
-} ArrayExprState;
-
-/* ----------------
- * RowExprState node
- * ----------------
- */
-typedef struct RowExprState
-{
- ExprState xprstate;
- List *args; /* the arguments */
- TupleDesc tupdesc; /* descriptor for result tuples */
-} RowExprState;
-
-/* ----------------
- * RowCompareExprState node
- * ----------------
- */
-typedef struct RowCompareExprState
-{
- ExprState xprstate;
- List *largs; /* the left-hand input arguments */
- List *rargs; /* the right-hand input arguments */
- FmgrInfo *funcs; /* array of comparison function info */
- Oid *collations; /* array of collations to use */
-} RowCompareExprState;
-
-/* ----------------
- * CoalesceExprState node
- * ----------------
- */
-typedef struct CoalesceExprState
-{
- ExprState xprstate;
- List *args; /* the arguments */
-} CoalesceExprState;
-
-/* ----------------
- * MinMaxExprState node
- * ----------------
- */
-typedef struct MinMaxExprState
-{
- ExprState xprstate;
- List *args; /* the arguments */
- FmgrInfo cfunc; /* lookup info for comparison func */
-} MinMaxExprState;
-
-/* ----------------
- * XmlExprState node
- * ----------------
- */
-typedef struct XmlExprState
-{
- ExprState xprstate;
- List *named_args; /* ExprStates for named arguments */
- List *args; /* ExprStates for other arguments */
-} XmlExprState;
-
-/* ----------------
- * NullTestState node
- * ----------------
- */
-typedef struct NullTestState
-{
- ExprState xprstate;
- ExprState *arg; /* input expression */
- /* used only if input is of composite type: */
- TupleDesc argdesc; /* tupdesc for most recent input */
-} NullTestState;
-
-/* ----------------
- * CoerceToDomainState node
- * ----------------
- */
-typedef struct CoerceToDomainState
-{
- ExprState xprstate;
- ExprState *arg; /* input expression */
- /* Cached set of constraints that need to be checked */
- /* use struct pointer to avoid including typcache.h here */
- struct DomainConstraintRef *constraint_ref;
-} CoerceToDomainState;
-
/*
* DomainConstraintState - one item to check during CoerceToDomain
*
NodeTag type;
DomainConstraintType constrainttype; /* constraint type */
char *name; /* name of constraint (for error msgs) */
- ExprState *check_expr; /* for CHECK, a boolean expression */
+ Expr *check_expr; /* for CHECK, a boolean expression */
+ ExprState *check_exprstate; /* check_expr's evaluation state */
} DomainConstraintState;
* state trees parallel links in the associated plan tree (except for the
* subPlan list, which does not exist in the plan tree).
*/
- List *targetlist; /* target list to be computed at this node */
- List *qual; /* implicitly-ANDed qual conditions */
+ ExprState *qual; /* implicitly-ANDed qual conditions */
struct PlanState *lefttree; /* input plan tree(s) */
struct PlanState *righttree;
List *initPlan; /* Init SubPlanState nodes (un-correlated expr
typedef struct ProjectSetState
{
PlanState ps; /* its first field is NodeTag */
+ Node **elems; /* array of expression states */
ExprDoneCond *elemdone; /* array of per-SRF is-done states */
int nelems; /* length of elemdone[] array */
bool pending_srf_tuples; /* still evaluating srfs in tlist? */
typedef struct IndexScanState
{
ScanState ss; /* its first field is NodeTag */
- List *indexqualorig;
+ ExprState *indexqualorig;
List *indexorderbyorig;
ScanKey iss_ScanKeys;
int iss_NumScanKeys;
typedef struct IndexOnlyScanState
{
ScanState ss; /* its first field is NodeTag */
- List *indexqual;
+ ExprState *indexqual;
ScanKey ioss_ScanKeys;
int ioss_NumScanKeys;
ScanKey ioss_OrderByKeys;
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
- List *bitmapqualorig;
+ ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
TBMIterateResult *tbmres;
typedef struct TidScanState
{
ScanState ss; /* its first field is NodeTag */
- List *tss_tidquals; /* list of ExprState nodes */
bool tss_isCurrentOf;
int tss_NumTids;
int tss_TidPtr;
typedef struct ForeignScanState
{
ScanState ss; /* its first field is NodeTag */
- List *fdw_recheck_quals; /* original quals not in ss.ps.qual */
+ ExprState *fdw_recheck_quals; /* original quals not in ss.ps.qual */
Size pscan_len; /* size of parallel coordination information */
/* use struct pointer to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine;
{
PlanState ps;
JoinType jointype;
- List *joinqual; /* JOIN quals (in addition to ps.qual) */
+ ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
} JoinState;
/* ----------------
typedef struct HashJoinState
{
JoinState js; /* its first field is NodeTag */
- List *hashclauses; /* list of ExprState nodes */
+ ExprState *hashclauses;
List *hj_OuterHashKeys; /* list of ExprState nodes */
List *hj_InnerHashKeys; /* list of ExprState nodes */
List *hj_HashOperators; /* list of operator OIDs */
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
*
- * These correspond (not always one-for-one) to primitive nodes derived
- * from Expr.
+ * ExprState represents the evaluation state for a whole expression tree.
+ * Most Expr based nodes do not have a corresponding expression state
+ * node, they're solely handled within execExpr* - but sometimes the state
+ * needs to be shared with other parts of the executor, as e.g. the case
+ * for AggrefExprState, which nodeAgg.c has to modify.
*/
T_ExprState,
- T_GenericExprState,
- T_WholeRowVarExprState,
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
- T_FuncExprState,
- T_ScalarArrayOpExprState,
- T_BoolExprState,
+ T_SetExprState,
T_SubPlanState,
T_AlternativeSubPlanState,
- T_FieldSelectState,
- T_FieldStoreState,
- T_CoerceViaIOState,
- T_ArrayCoerceExprState,
- T_ConvertRowtypeExprState,
- T_CaseExprState,
- T_CaseWhenState,
- T_ArrayExprState,
- T_RowExprState,
- T_RowCompareExprState,
- T_CoalesceExprState,
- T_MinMaxExprState,
- T_XmlExprState,
- T_NullTestState,
- T_CoerceToDomainState,
T_DomainConstraintState,
/*
const char *msg);
extern xmltype *xmlconcat(List *args);
-extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
+extern xmltype *xmlelement(XmlExpr *xexpr, bool *named_argnull, Datum *named_argvalue, bool *argnull, Datum *argvalue);
extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace);
extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
extern xmltype *xmlroot(xmltype *data, text *version, int standalone);
{
plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */
Expr *cast_expr; /* cast expression, or NULL if no-op cast */
- /* The ExprState tree is valid only when cast_lxid matches current LXID */
+ /* ExprState is valid only when cast_lxid matches current LXID */
ExprState *cast_exprstate; /* expression's eval tree */
bool cast_in_use; /* true while we're executing eval tree */
LocalTransactionId cast_lxid;
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like the expression built by ExecInitArrayRef(), complain
+ * if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
-- Nested CASE expressions
--
-- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call. After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation. After
-- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
-- the isNull flag for the case test value incorrectly became true, causing
-- the third WHEN-clause not to match. The volatile function calls are needed
--
-- This test exercises a bug caused by aliasing econtext->caseValue_isNull
--- with the isNull argument of the inner CASE's ExecEvalCase() call. After
+-- with the isNull argument of the inner CASE's CaseExpr evaluation. After
-- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
-- the isNull flag for the case test value incorrectly became true, causing
-- the third WHEN-clause not to match. The volatile function calls are needed
ExprDoneCond
ExprState
ExprStateEvalFunc
+ExprEvalOp
+ExprEvalStep
ExtensibleNode
ExtensibleNodeEntry
ExtensibleNodeMethods
SetConstraintState
SetConstraintStateData
SetConstraintTriggerData
+SetExprState
SetFunctionReturnMode
SetOp
SetOpCmd