--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execExpr.c
+ * Expression evaluation infrastructure.
+ *
+ * During executor startup, we compile each expression tree (which has
+ * previously been processed by the parser and planner) into an ExprState,
+ * using ExecInitExpr() et al. This converts the tree into a flat array
+ * of ExprEvalSteps, which may be thought of as instructions in a program.
+ * At runtime, we'll execute steps, starting with the first, until we reach
+ * an EEOP_DONE opcode.
+ *
+ * This file contains the "compilation" logic. It is independent of the
+ * specific execution technology we use (switch statement, computed goto,
+ * JIT compilation, etc).
+ *
+ * See src/backend/executor/README for some background, specifically the
+ * "Expression Trees and ExprState nodes", "Expression Initialization",
+ * and "Expession Evaluation" sections.
+ *
+ *
+ * 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/nbtree.h"
+#include "catalog/objectaccess.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.h"
+#include "funcapi.h"
+#include "miscadmin.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
+#include "optimizer/clauses.h"
+#include "optimizer/planner.h"
+#include "pgstat.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/typcache.h"
+
+
+typedef struct LastAttnumInfo
+{
+ AttrNumber last_inner;
+ AttrNumber last_outer;
+ AttrNumber last_scan;
+} LastAttnumInfo;
+
+static void ExecReadyExpr(ExprState *state);
+static void ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static void ExprEvalPushStep(ExprState *es, const ExprEvalStep *s);
+static void ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args,
+ Oid funcid, Oid inputcollid, PlanState *parent,
+ ExprState *state);
+static void ExecInitExprSlots(ExprState *state, Node *node);
+static bool get_last_attnums_walker(Node *node, LastAttnumInfo *info);
+static void ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable,
+ PlanState *parent);
+static void ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+static bool isAssignmentIndirectionExpr(Expr *expr);
+static void ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull);
+
+
+/*
+ * 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. (But note that an ExprState
+ * does mutate at runtime, so it can't be re-used 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 (or more accurately,
+ * the AggrefExprState etc. nodes created for them are added).
+ *
+ * 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 compile.
+ * '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.)
+ * Such cases should usually come through ExecPrepareExpr, not directly here.
+ *
+ * Also, if 'node' is NULL, we just return NULL. This is convenient for some
+ * callers that may or may not have an expression that needs to be compiled.
+ * Note that a NULL ExprState pointer *cannot* be handed to ExecEvalExpr,
+ * although ExecQual and ExecCheck will accept one (and treat it as "true").
+ */
+ExprState *
+ExecInitExpr(Expr *node, PlanState *parent)
+{
+ ExprState *state;
+ ExprEvalStep scratch;
+
+ /* Special case: NULL expression produces a NULL ExprState pointer */
+ if (node == NULL)
+ return NULL;
+
+ /* Initialize ExprState with empty step list */
+ state = makeNode(ExprState);
+ state->expr = node;
+
+ /* Insert EEOP_*_FETCHSOME steps as needed */
+ ExecInitExprSlots(state, (Node *) node);
+
+ /* Compile the expression proper */
+ ExecInitExprRec(node, parent, state, &state->resvalue, &state->resnull);
+
+ /* Finally, append a DONE step */
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
+}
+
+/*
+ * ExecInitQual: prepare a qual for execution by ExecQual
+ *
+ * Prepares for the evaluation of a conjunctive boolean expression (qual list
+ * with implicit AND semantics) that returns true if none of the
+ * subexpressions are false.
+ *
+ * We must return true if the list is empty. Since that's a very common case,
+ * we optimize it a bit further by translating to a NULL ExprState pointer
+ * rather than setting up an ExprState that computes constant TRUE. (Some
+ * especially hot-spot callers of ExecQual detect this and avoid calling
+ * ExecQual at all.)
+ *
+ * If any of the subexpressions yield NULL, then the result of the conjunction
+ * is false. This makes ExecQual 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;
+ ExprEvalStep scratch;
+ List *adjust_jumps = NIL;
+ ListCell *lc;
+
+ /* short-circuit (here and in ExecQual) for empty restriction list */
+ if (qual == NIL)
+ return NULL;
+
+ Assert(IsA(qual, List));
+
+ state = makeNode(ExprState);
+ state->expr = (Expr *) qual;
+ /* mark expression as to be used with ExecQual() */
+ state->flags = EEO_FLAG_IS_QUAL;
+
+ /* Insert EEOP_*_FETCHSOME steps as needed */
+ ExecInitExprSlots(state, (Node *) qual);
+
+ /*
+ * ExecQual() needs to return false for an expression returning NULL. That
+ * allows us 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).
+ */
+ scratch.opcode = EEOP_QUAL;
+
+ /*
+ * We can use ExprState's resvalue/resnull as target for each qual expr.
+ */
+ 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 emit EEOP_QUAL to detect if it's false (or null) */
+ scratch.d.qualexpr.jumpdone = -1;
+ ExprEvalPushStep(state, &scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ }
+
+ /* adjust jump targets */
+ foreach(lc, adjust_jumps)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->opcode == EEOP_QUAL);
+ Assert(as->d.qualexpr.jumpdone == -1);
+ as->d.qualexpr.jumpdone = state->steps_len;
+ }
+
+ /*
+ * At the end, we don't need to do anything more. The last qual expr must
+ * have yielded TRUE, and since its result is stored in the desired output
+ * location, we're done.
+ */
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return state;
+}
+
+/*
+ * ExecInitCheck: prepare a check constraint for execution by ExecCheck
+ *
+ * This is much like ExecInitQual/ExecQual, except that a null result from
+ * the conjunction is treated as TRUE. This behavior is appropriate for
+ * evaluating CHECK constraints, since SQL specifies that NULL constraint
+ * conditions are not failures.
+ *
+ * Note that like ExecInitQual, this expects input in implicit-AND format.
+ * Users of ExecCheck that have expressions in normal explicit-AND format
+ * can just apply ExecInitExpr to produce suitable input for ExecCheck.
+ */
+ExprState *
+ExecInitCheck(List *qual, PlanState *parent)
+{
+ /* short-circuit (here and in ExecCheck) for empty restriction list */
+ if (qual == NIL)
+ return NULL;
+
+ Assert(IsA(qual, List));
+
+ /*
+ * Just convert the implicit-AND list to an explicit AND (if there's more
+ * than one entry), and compile normally. Unlike ExecQual, we can't
+ * short-circuit on NULL results, so the regular AND behavior is needed.
+ */
+ return ExecInitExpr(make_ands_explicit(qual), parent);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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!)
+ *
+ * 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
+ * whole projection in one go.
+ *
+ * Caution: before PG v10, the targetList was a list of ExprStates; now it
+ * should be the planner-created targetlist, since we do the compilation here.
+ */
+ProjectionInfo *
+ExecBuildProjectionInfo(List *targetList,
+ ExprContext *econtext,
+ TupleTableSlot *slot,
+ PlanState *parent,
+ TupleDesc inputDesc)
+{
+ ProjectionInfo *projInfo = makeNode(ProjectionInfo);
+ ExprState *state;
+ ExprEvalStep scratch;
+ ListCell *lc;
+
+ projInfo->pi_exprContext = econtext;
+ /* We embed ExprState into ProjectionInfo instead of doing extra palloc */
+ projInfo->pi_state.tag.type = T_ExprState;
+ state = &projInfo->pi_state;
+ state->expr = (Expr *) targetList;
+ state->resultslot = slot;
+
+ /* Insert EEOP_*_FETCHSOME steps as needed */
+ ExecInitExprSlots(state, (Node *) targetList);
+
+ /* Now compile each tlist column */
+ foreach(lc, targetList)
+ {
+ TargetEntry *tle = castNode(TargetEntry, lfirst(lc));
+ Var *variable = NULL;
+ AttrNumber attnum = 0;
+ bool isSafeVar = false;
+
+ /*
+ * If tlist expression is a safe non-system Var, use the fast-path
+ * ASSIGN_*_VAR opcodes. "Safe" means that we don't need to apply
+ * CheckVarSlotCompatibility() during plan startup. If a source slot
+ * was provided, we make the equivalent tests here; if a slot was not
+ * provided, we assume that no check is needed because we're dealing
+ * with a non-relation-scan-level expression.
+ */
+ if (tle->expr != NULL &&
+ IsA(tle->expr, Var) &&
+ ((Var *) tle->expr)->varattno > 0)
+ {
+ /* Non-system Var, but how safe is it? */
+ variable = (Var *) tle->expr;
+ attnum = variable->varattno;
+
+ if (inputDesc == NULL)
+ isSafeVar = true; /* can't check, just assume OK */
+ else if (attnum <= inputDesc->natts)
+ {
+ Form_pg_attribute attr = inputDesc->attrs[attnum - 1];
+
+ /*
+ * If user attribute is dropped or has a type mismatch, don't
+ * use ASSIGN_*_VAR. Instead let the normal expression
+ * machinery handle it (which'll possibly error out).
+ */
+ if (!attr->attisdropped && variable->vartype == attr->atttypid)
+ {
+ isSafeVar = true;
+ }
+ }
+ }
+
+ if (isSafeVar)
+ {
+ /* Fast-path: just generate an EEOP_ASSIGN_*_VAR step */
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ /* get the tuple from the inner node */
+ scratch.opcode = EEOP_ASSIGN_INNER_VAR;
+ break;
+
+ case OUTER_VAR:
+ /* get the tuple from the outer node */
+ scratch.opcode = EEOP_ASSIGN_OUTER_VAR;
+ break;
+
+ /* INDEX_VAR is handled by default case */
+
+ default:
+ /* get the tuple from the relation being scanned */
+ scratch.opcode = EEOP_ASSIGN_SCAN_VAR;
+ break;
+ }
+
+ scratch.d.assign_var.attnum = attnum - 1;
+ scratch.d.assign_var.resultnum = tle->resno - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ else
+ {
+ /*
+ * Otherwise, compile the column expression normally.
+ *
+ * We can't tell the expression to evaluate directly into the
+ * result slot, as the result slot (and the exprstate for that
+ * matter) can change between executions. We instead evaluate
+ * into the ExprState's resvalue/resnull and then move.
+ */
+ ExecInitExprRec(tle->expr, parent, state,
+ &state->resvalue, &state->resnull);
+
+ /*
+ * Column might be referenced multiple times in upper nodes, so
+ * force value to R/O - but only if it could be an expanded datum.
+ */
+ if (get_typlen(exprType((Node *) tle->expr)) == -1)
+ scratch.opcode = EEOP_ASSIGN_TMP_MAKE_RO;
+ else
+ scratch.opcode = EEOP_ASSIGN_TMP;
+ scratch.d.assign_tmp.resultnum = tle->resno - 1;
+ ExprEvalPushStep(state, &scratch);
+ }
+ }
+
+ scratch.opcode = EEOP_DONE;
+ ExprEvalPushStep(state, &scratch);
+
+ ExecReadyExpr(state);
+
+ return projInfo;
+}
+
+/*
+ * 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 ExecInitQual 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 check constraint for execution outside a
+ * normal Plan tree context.
+ *
+ * See ExecPrepareExpr() and ExecInitCheck() 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 a list of Exprs, and return
+ * a list of ExprStates.
+ *
+ * See ExecPrepareExpr() for details.
+ */
+List *
+ExecPrepareExprList(List *nodes, EState *estate)
+{
+ List *result = NIL;
+ ListCell *lc;
+
+ foreach(lc, nodes)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ result = lappend(result, ExecPrepareExpr(e, estate));
+ }
+
+ return result;
+}
+
+/*
+ * ExecCheck - evaluate a check constraint
+ *
+ * For check constraints, a null result is taken as TRUE, ie the constraint
+ * passes.
+ *
+ * The check constraint may have been prepared with ExecInitCheck
+ * (possibly via ExecPrepareCheck) if the caller had it in implicit-AND
+ * format, but a regular boolean expression prepared with ExecInitExpr or
+ * ExecPrepareExpr works too.
+ */
+bool
+ExecCheck(ExprState *state, ExprContext *econtext)
+{
+ Datum ret;
+ bool isnull;
+
+ /* short-circuit (here and in ExecInitCheck) for empty restriction list */
+ if (state == NULL)
+ return true;
+
+ /* verify that expression was not compiled using ExecInitQual */
+ Assert(!(state->flags & EEO_FLAG_IS_QUAL));
+
+ ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
+
+ if (isnull)
+ return true;
+
+ return DatumGetBool(ret);
+}
+
+/*
+ * Prepare a compiled expression for execution. This has to be called for
+ * every ExprState before it can be executed.
+ *
+ * NB: While this currently only calls ExecReadyInterpretedExpr(),
+ * this will likely get extended to further expression evaluation methods.
+ * Therefore this should be used instead of directly calling
+ * ExecReadyInterpretedExpr().
+ */
+static void
+ExecReadyExpr(ExprState *state)
+{
+ ExecReadyInterpretedExpr(state);
+}
+
+/*
+ * Append the steps necessary for the evaluation of node to ExprState->steps,
+ * possibly recursing into sub-expressions of node.
+ *
+ * node - expression to evaluate
+ * parent - parent executor node (or NULL if a standalone expression)
+ * state - ExprState to whose ->steps to append the necessary operations
+ * resv / resnull - where to store the result of the node into
+ */
+static void
+ExecInitExprRec(Expr *node, PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull)
+{
+ ExprEvalStep scratch;
+
+ /* Guard against stack overflow due to overly complex expressions */
+ check_stack_depth();
+
+ /* Step's output location is always what the caller gave us */
+ 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;
+
+ if (variable->varattno == InvalidAttrNumber)
+ {
+ /* whole-row Var */
+ ExecInitWholeRowVar(&scratch, variable, parent);
+ }
+ else if (variable->varattno <= 0)
+ {
+ /* system column */
+ scratch.d.var.attnum = variable->varattno;
+ scratch.d.var.vartype = variable->vartype;
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ scratch.opcode = EEOP_INNER_SYSVAR;
+ break;
+ case OUTER_VAR:
+ scratch.opcode = EEOP_OUTER_SYSVAR;
+ break;
+
+ /* INDEX_VAR is handled by default case */
+
+ default:
+ scratch.opcode = EEOP_SCAN_SYSVAR;
+ break;
+ }
+ }
+ else
+ {
+ /* regular user column */
+ scratch.d.var.attnum = variable->varattno - 1;
+ scratch.d.var.vartype = variable->vartype;
+ /* select EEOP_*_FIRST opcode to force one-time checks */
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ scratch.opcode = EEOP_INNER_VAR_FIRST;
+ break;
+ case OUTER_VAR:
+ scratch.opcode = EEOP_OUTER_VAR_FIRST;
+ break;
+
+ /* INDEX_VAR is handled by default case */
+
+ default:
+ scratch.opcode = EEOP_SCAN_VAR_FIRST;
+ break;
+ }
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_Const:
+ {
+ Const *con = (Const *) node;
+
+ scratch.opcode = EEOP_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 = EEOP_PARAM_EXEC;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ break;
+ case PARAM_EXTERN:
+ scratch.opcode = EEOP_PARAM_EXTERN;
+ scratch.d.param.paramid = param->paramid;
+ scratch.d.param.paramtype = param->paramtype;
+ break;
+ default:
+ elog(ERROR, "unrecognized paramkind: %d",
+ (int) param->paramkind);
+ break;
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_Aggref:
+ {
+ Aggref *aggref = (Aggref *) node;
+ AggrefExprState *astate = makeNode(AggrefExprState);
+
+ scratch.opcode = EEOP_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;
+
+ if (!parent || !IsA(parent, AggState) ||
+ !IsA(parent->plan, Agg))
+ elog(ERROR, "GroupingFunc found in non-Agg plan node");
+
+ scratch.opcode = EEOP_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 initialize agg using old style expressions */
+ 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 = EEOP_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);
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_OpExpr:
+ {
+ OpExpr *op = (OpExpr *) node;
+
+ ExecInitFunc(&scratch, node,
+ op->args, op->opfuncid, op->inputcollid,
+ parent, state);
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_DistinctExpr:
+ {
+ DistinctExpr *op = (DistinctExpr *) node;
+
+ ExecInitFunc(&scratch, node,
+ op->args, op->opfuncid, op->inputcollid,
+ parent, state);
+
+ /*
+ * Change opcode of call instruction to EEOP_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. If
+ * we decided to do that here, we'd probably want separate
+ * opcodes for FUSAGE or not.
+ */
+ scratch.opcode = EEOP_DISTINCT;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_NullIfExpr:
+ {
+ NullIfExpr *op = (NullIfExpr *) node;
+
+ ExecInitFunc(&scratch, node,
+ op->args, op->opfuncid, op->inputcollid,
+ parent, state);
+
+ /*
+ * Change opcode of call instruction to EEOP_NULLIF.
+ *
+ * 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. If
+ * we decided to do that here, we'd probably want separate
+ * opcodes for FUSAGE or not.
+ */
+ scratch.opcode = EEOP_NULLIF;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ScalarArrayOpExpr:
+ {
+ ScalarArrayOpExpr *opexpr = (ScalarArrayOpExpr *) node;
+ Expr *scalararg;
+ Expr *arrayarg;
+ FmgrInfo *finfo;
+ FunctionCallInfo fcinfo;
+ AclResult aclresult;
+
+ Assert(list_length(opexpr->args) == 2);
+ scalararg = (Expr *) linitial(opexpr->args);
+ arrayarg = (Expr *) lsecond(opexpr->args);
+
+ /* Check permission to call function */
+ aclresult = pg_proc_aclcheck(opexpr->opfuncid,
+ GetUserId(),
+ ACL_EXECUTE);
+ if (aclresult != ACLCHECK_OK)
+ aclcheck_error(aclresult, ACL_KIND_PROC,
+ get_func_name(opexpr->opfuncid));
+ InvokeFunctionExecuteHook(opexpr->opfuncid);
+
+ /* Set up the primary fmgr lookup information */
+ 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);
+
+ /* Evaluate scalar directly into left function argument */
+ ExecInitExprRec(scalararg, parent, state,
+ &fcinfo->arg[0], &fcinfo->argnull[0]);
+
+ /*
+ * Evaluate array argument into our return value. There's no
+ * danger in that, because the return value is guaranteed to
+ * be overwritten by EEOP_SCALARARRAYOP, and will not be
+ * passed to any other expression.
+ */
+ ExecInitExprRec(arrayarg, parent, state, resv, resnull);
+
+ /* And perform the operation */
+ scratch.opcode = EEOP_SCALARARRAYOP;
+ scratch.d.scalararrayop.element_type = InvalidOid;
+ scratch.d.scalararrayop.useOr = opexpr->useOr;
+ scratch.d.scalararrayop.finfo = finfo;
+ scratch.d.scalararrayop.fcinfo_data = fcinfo;
+ scratch.d.scalararrayop.fn_addr = finfo->fn_addr;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_BoolExpr:
+ {
+ BoolExpr *boolexpr = (BoolExpr *) node;
+ int nargs = list_length(boolexpr->args);
+ List *adjust_jumps = NIL;
+ int off;
+ ListCell *lc;
+
+ /* allocate scratch memory used by all steps of AND/OR */
+ if (boolexpr->boolop != NOT_EXPR)
+ scratch.d.boolexpr.anynull = (bool *) palloc(sizeof(bool));
+
+ /*
+ * For each argument evaluate the argument itself, then
+ * perform the bool operation's appropriate handling.
+ *
+ * We can evaluate each argument into our result area, since
+ * the short-circuiting logic means we only need to remember
+ * previous NULL values.
+ *
+ * AND/OR is split into separate STEP_FIRST (one) / STEP (zero
+ * or more) / STEP_LAST (one) steps, as each of those has to
+ * perform different work. The FIRST/LAST split is valid
+ * because AND/OR have at least two arguments.
+ */
+ off = 0;
+ foreach(lc, boolexpr->args)
+ {
+ Expr *arg = (Expr *) lfirst(lc);
+
+ /* Evaluate argument into our output variable */
+ ExecInitExprRec(arg, parent, state, resv, resnull);
+
+ /* Perform the appropriate step type */
+ switch (boolexpr->boolop)
+ {
+ case AND_EXPR:
+ Assert(nargs >= 2);
+
+ if (off == 0)
+ scratch.opcode = EEOP_BOOL_AND_STEP_FIRST;
+ else if (off + 1 == nargs)
+ scratch.opcode = EEOP_BOOL_AND_STEP_LAST;
+ else
+ scratch.opcode = EEOP_BOOL_AND_STEP;
+ break;
+ case OR_EXPR:
+ Assert(nargs >= 2);
+
+ if (off == 0)
+ scratch.opcode = EEOP_BOOL_OR_STEP_FIRST;
+ else if (off + 1 == nargs)
+ scratch.opcode = EEOP_BOOL_OR_STEP_LAST;
+ else
+ scratch.opcode = EEOP_BOOL_OR_STEP;
+ break;
+ case NOT_EXPR:
+ Assert(nargs == 1);
+
+ scratch.opcode = EEOP_BOOL_NOT_STEP;
+ break;
+ default:
+ elog(ERROR, "unrecognized boolop: %d",
+ (int) boolexpr->boolop);
+ break;
+ }
+
+ scratch.d.boolexpr.jumpdone = -1;
+ ExprEvalPushStep(state, &scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ off++;
+ }
+
+ /* adjust jump targets */
+ foreach(lc, adjust_jumps)
+ {
+ 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 = EEOP_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 = EEOP_ALTERNATIVE_SUBPLAN;
+ scratch.d.alternative_subplan.asstate = asstate;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_FieldSelect:
+ {
+ FieldSelect *fselect = (FieldSelect *) node;
+
+ /* evaluate row/record argument into result area */
+ ExecInitExprRec(fselect->arg, parent, state, resv, resnull);
+
+ /* and extract field */
+ scratch.opcode = EEOP_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;
+ TupleDesc tupDesc;
+ TupleDesc *descp;
+ Datum *values;
+ bool *nulls;
+ int ncolumns;
+ ListCell *l1,
+ *l2;
+
+ /* find out the number of columns in the composite type */
+ tupDesc = lookup_rowtype_tupdesc(fstore->resulttype, -1);
+ ncolumns = tupDesc->natts;
+ DecrTupleDescRefCount(tupDesc);
+
+ /* create workspace for column values */
+ values = (Datum *) palloc(sizeof(Datum) * ncolumns);
+ nulls = (bool *) palloc(sizeof(bool) * ncolumns);
+
+ /* create workspace for runtime tupdesc cache */
+ descp = (TupleDesc *) palloc(sizeof(TupleDesc));
+ *descp = NULL;
+
+ /* emit code to evaluate the composite input value */
+ ExecInitExprRec(fstore->arg, parent, state, resv, resnull);
+
+ /* next, deform the input tuple into our workspace */
+ scratch.opcode = EEOP_FIELDSTORE_DEFORM;
+ scratch.d.fieldstore.fstore = fstore;
+ scratch.d.fieldstore.argdesc = descp;
+ scratch.d.fieldstore.values = values;
+ scratch.d.fieldstore.nulls = nulls;
+ scratch.d.fieldstore.ncolumns = ncolumns;
+ ExprEvalPushStep(state, &scratch);
+
+ /* evaluate new field values, store in workspace columns */
+ forboth(l1, fstore->newvals, l2, fstore->fieldnums)
+ {
+ Expr *e = (Expr *) lfirst(l1);
+ AttrNumber fieldnum = lfirst_int(l2);
+ Datum *save_innermost_caseval;
+ bool *save_innermost_casenull;
+
+ if (fieldnum <= 0 || fieldnum > ncolumns)
+ elog(ERROR, "field number %d is out of range in FieldStore",
+ fieldnum);
+
+ /*
+ * 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 innermost_caseval 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;
+ }
+
+ /* finally, form result tuple */
+ scratch.opcode = EEOP_FIELDSTORE_FORM;
+ scratch.d.fieldstore.fstore = fstore;
+ scratch.d.fieldstore.argdesc = descp;
+ scratch.d.fieldstore.values = values;
+ scratch.d.fieldstore.nulls = nulls;
+ scratch.d.fieldstore.ncolumns = ncolumns;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_RelabelType:
+ {
+ /* relabel doesn't need to do anything at runtime */
+ RelabelType *relabel = (RelabelType *) node;
+
+ ExecInitExprRec(relabel->arg, parent, state, resv, resnull);
+ break;
+ }
+
+ case T_CoerceViaIO:
+ {
+ CoerceViaIO *iocoerce = (CoerceViaIO *) node;
+ Oid iofunc;
+ bool typisvarlena;
+ Oid typioparam;
+ FunctionCallInfo fcinfo_in;
+
+ /* evaluate argument into step's result area */
+ ExecInitExprRec(iocoerce->arg, parent, state, resv, resnull);
+
+ /*
+ * Prepare both output and input function calls, to be
+ * evaluated inside a single evaluation step for speed - this
+ * can be a very common operation.
+ *
+ * We don't check permissions here as a type's input/output
+ * function are assumed to be executable by everyone.
+ */
+ scratch.opcode = EEOP_IOCOERCE;
+
+ /* lookup the source type's output function */
+ scratch.d.iocoerce.finfo_out = palloc0(sizeof(FmgrInfo));
+ scratch.d.iocoerce.fcinfo_data_out = palloc0(sizeof(FunctionCallInfoData));
+
+ 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(FmgrInfo));
+ scratch.d.iocoerce.fcinfo_data_in = palloc0(sizeof(FunctionCallInfoData));
+
+ getTypeInputInfo(iocoerce->resulttype,
+ &iofunc, &typioparam);
+ 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);
+
+ /*
+ * We can preload the second and third arguments for the input
+ * function, since they're constants.
+ */
+ fcinfo_in = scratch.d.iocoerce.fcinfo_data_in;
+ fcinfo_in->arg[1] = ObjectIdGetDatum(typioparam);
+ fcinfo_in->argnull[1] = false;
+ fcinfo_in->arg[2] = Int32GetDatum(-1);
+ fcinfo_in->argnull[2] = false;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ArrayCoerceExpr:
+ {
+ ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
+ Oid resultelemtype;
+
+ /* evaluate argument into step's result area */
+ ExecInitExprRec(acoerce->arg, parent, state, resv, resnull);
+
+ resultelemtype = get_element_type(acoerce->resulttype);
+ if (!OidIsValid(resultelemtype))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("target type is not an array")));
+ /* Arrays over domains aren't supported yet */
+ Assert(getBaseType(resultelemtype) == resultelemtype);
+
+ scratch.opcode = EEOP_ARRAYCOERCE;
+ scratch.d.arraycoerce.coerceexpr = acoerce;
+ scratch.d.arraycoerce.resultelemtype = resultelemtype;
+
+ if (OidIsValid(acoerce->elemfuncid))
+ {
+ 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 */
+ scratch.d.arraycoerce.elemfunc =
+ (FmgrInfo *) palloc0(sizeof(FmgrInfo));
+ fmgr_info(acoerce->elemfuncid,
+ scratch.d.arraycoerce.elemfunc);
+ fmgr_info_set_expr((Node *) acoerce,
+ scratch.d.arraycoerce.elemfunc);
+
+ /* Set up workspace for array_map */
+ scratch.d.arraycoerce.amstate =
+ (ArrayMapState *) palloc0(sizeof(ArrayMapState));
+ }
+ else
+ {
+ /* Don't need workspace if there's no conversion func */
+ scratch.d.arraycoerce.elemfunc = NULL;
+ scratch.d.arraycoerce.amstate = NULL;
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_ConvertRowtypeExpr:
+ {
+ ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
+
+ /* evaluate argument into step's result area */
+ ExecInitExprRec(convert->arg, parent, state, resv, resnull);
+
+ /* and push conversion step */
+ scratch.opcode = EEOP_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;
+ List *adjust_jumps = NIL;
+ Datum *caseval = NULL;
+ bool *casenull = NULL;
+ ListCell *lc;
+
+ /*
+ * If there's a test expression, we have to evaluate it and
+ * save the value where the CaseTestExpr placeholders can find
+ * it.
+ */
+ if (caseExpr->arg != NULL)
+ {
+ /* Evaluate testexpr into caseval/casenull workspace */
+ caseval = palloc(sizeof(Datum));
+ casenull = palloc(sizeof(bool));
+
+ ExecInitExprRec(caseExpr->arg, parent, state,
+ caseval, casenull);
+
+ /*
+ * Since value might be read multiple times, force to R/O
+ * - but only if it could be an expanded datum.
+ */
+ if (get_typlen(exprType((Node *) caseExpr->arg)) == -1)
+ {
+ /* change caseval in-place */
+ scratch.opcode = EEOP_MAKE_READONLY;
+ scratch.resvalue = caseval;
+ scratch.resnull = casenull;
+ scratch.d.make_readonly.value = caseval;
+ scratch.d.make_readonly.isnull = casenull;
+ ExprEvalPushStep(state, &scratch);
+ /* restore normal settings of scratch fields */
+ scratch.resvalue = resv;
+ scratch.resnull = resnull;
+ }
+ }
+
+ /*
+ * Prepare to evaluate each of the WHEN clauses in turn; as
+ * soon as one is true we return the value of the
+ * corresponding THEN clause. If none are true then we return
+ * the value of the ELSE clause, or NULL if there is none.
+ */
+ foreach(lc, caseExpr->args)
+ {
+ CaseWhen *when = (CaseWhen *) lfirst(lc);
+ Datum *save_innermost_caseval;
+ bool *save_innermost_casenull;
+ int whenstep;
+
+ /*
+ * Make testexpr result available to CaseTestExpr nodes
+ * within the condition. We must save and restore prior
+ * setting of innermost_caseval 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.
+ */
+ save_innermost_caseval = state->innermost_caseval;
+ save_innermost_casenull = state->innermost_casenull;
+ state->innermost_caseval = caseval;
+ state->innermost_casenull = casenull;
+
+ /* evaluate condition into CASE's result variables */
+ ExecInitExprRec(when->expr, parent, state, resv, resnull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+
+ /* If WHEN result isn't true, jump to next CASE arm */
+ scratch.opcode = EEOP_JUMP_IF_NOT_TRUE;
+ scratch.d.jump.jumpdone = -1; /* computed later */
+ ExprEvalPushStep(state, &scratch);
+ whenstep = state->steps_len - 1;
+
+ /*
+ * If WHEN result is true, evaluate THEN result, storing
+ * it into the CASE's result variables.
+ */
+ ExecInitExprRec(when->result, parent, state, resv, resnull);
+
+ /* Emit JUMP step to jump to end of CASE's code */
+ scratch.opcode = EEOP_JUMP;
+ scratch.d.jump.jumpdone = -1; /* computed later */
+ ExprEvalPushStep(state, &scratch);
+
+ /*
+ * Don't know address for that jump yet, compute once the
+ * whole CASE expression is built.
+ */
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+
+ /*
+ * But we can set WHEN test's jump target now, to make it
+ * jump to the next WHEN subexpression or the ELSE.
+ */
+ state->steps[whenstep].d.jump.jumpdone = state->steps_len;
+ }
+
+ if (caseExpr->defresult)
+ {
+ /* evaluate ELSE expr into CASE's result variables */
+ ExecInitExprRec(caseExpr->defresult, parent, state,
+ resv, resnull);
+ }
+ else
+ {
+ /* default ELSE is to return NULL */
+ scratch.opcode = EEOP_CONST;
+ scratch.d.constval.value = (Datum) 0;
+ scratch.d.constval.isnull = true;
+ ExprEvalPushStep(state, &scratch);
+ }
+
+ /* adjust jump targets */
+ foreach(lc, adjust_jumps)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->opcode == EEOP_JUMP);
+ Assert(as->d.jump.jumpdone == -1);
+ as->d.jump.jumpdone = state->steps_len;
+ }
+
+ break;
+ }
+
+ case T_CaseTestExpr:
+ {
+ /*
+ * Read from location identified by innermost_caseval. Note
+ * that innermost_caseval could be NULL, if this node isn't
+ * actually within a CASE structure; some parts of the system
+ * abuse CaseTestExpr to cause a read of a value externally
+ * supplied in econtext->caseValue_datum. We'll take care of
+ * that scenario at runtime.
+ */
+ scratch.opcode = EEOP_CASE_TESTVAL;
+ scratch.d.casetest.value = state->innermost_caseval;
+ scratch.d.casetest.isnull = state->innermost_casenull;
+
+ 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. Elements are computed into scratch arrays
+ * associated with the ARRAYEXPR step.
+ */
+ scratch.opcode = EEOP_ARRAYEXPR;
+ scratch.d.arrayexpr.elemvalues =
+ (Datum *) palloc(sizeof(Datum) * nelems);
+ scratch.d.arrayexpr.elemnulls =
+ (bool *) palloc(sizeof(bool) * nelems);
+ scratch.d.arrayexpr.nelems = nelems;
+
+ /* fill remaining fields of step */
+ scratch.d.arrayexpr.multidims = arrayexpr->multidims;
+ scratch.d.arrayexpr.elemtype = arrayexpr->element_typeid;
+
+ /* 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 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;
+
+ /* 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);
+ }
+ /* 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);
+
+ /*
+ * In the named-type case, the tupdesc could have more columns
+ * than are in the args list, since the type might have had
+ * columns added since the ROW() was parsed. We want those
+ * extra columns to go to nulls, so we make sure that the
+ * workspace arrays are large enough and then initialize any
+ * extra columns to read as NULLs.
+ */
+ Assert(nelems <= tupdesc->natts);
+ nelems = Max(nelems, tupdesc->natts);
+
+ /*
+ * Evaluate by first building datums for each field, and then
+ * a final step forming the composite datum.
+ */
+ scratch.opcode = EEOP_ROW;
+ scratch.d.row.tupdesc = tupdesc;
+
+ /* space for the individual field datums */
+ scratch.d.row.elemvalues =
+ (Datum *) palloc(sizeof(Datum) * nelems);
+ scratch.d.row.elemnulls =
+ (bool *) palloc(sizeof(bool) * nelems);
+ /* as explained above, make sure any extra columns are null */
+ memset(scratch.d.row.elemnulls, true, sizeof(bool) * nelems);
+
+ /* 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);
+ }
+
+ /* Evaluate column expr into appropriate workspace slot */
+ ExecInitExprRec(e, parent, state,
+ &scratch.d.row.elemvalues[i],
+ &scratch.d.row.elemnulls[i]);
+ i++;
+ }
+
+ /* And finally build the row value */
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_RowCompareExpr:
+ {
+ RowCompareExpr *rcexpr = (RowCompareExpr *) node;
+ int nopers = list_length(rcexpr->opnos);
+ List *adjust_jumps = 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 a
+ * comparison yields a != 0 result, jump to the final step.
+ */
+ Assert(list_length(rcexpr->largs) == nopers);
+ Assert(list_length(rcexpr->rargs) == nopers);
+ Assert(list_length(rcexpr->opfamilies) == nopers);
+ Assert(list_length(rcexpr->inputcollids) == 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 args 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 = EEOP_ROWCOMPARE_STEP;
+ scratch.d.rowcompare_step.finfo = finfo;
+ scratch.d.rowcompare_step.fcinfo_data = fcinfo;
+ scratch.d.rowcompare_step.fn_addr = finfo->fn_addr;
+ /* jump targets filled below */
+ scratch.d.rowcompare_step.jumpnull = -1;
+ scratch.d.rowcompare_step.jumpdone = -1;
+
+ ExprEvalPushStep(state, &scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ }
+
+ /*
+ * We could have a zero-column rowtype, in which case the rows
+ * necessarily compare equal.
+ */
+ if (nopers == 0)
+ {
+ scratch.opcode = EEOP_CONST;
+ scratch.d.constval.value = Int32GetDatum(0);
+ scratch.d.constval.isnull = false;
+ ExprEvalPushStep(state, &scratch);
+ }
+
+ /* Finally, examine the last comparison result */
+ scratch.opcode = EEOP_ROWCOMPARE_FINAL;
+ scratch.d.rowcompare_final.rctype = rcexpr->rctype;
+ ExprEvalPushStep(state, &scratch);
+
+ /* adjust jump targetss */
+ foreach(lc, adjust_jumps)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->opcode == EEOP_ROWCOMPARE_STEP);
+ 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_jumps = NIL;
+ ListCell *lc;
+
+ /* We assume there's at least one arg */
+ Assert(coalesce->args != NIL);
+
+ /*
+ * Prepare evaluation of all coalesced arguments, after each
+ * one push a step that short-circuits if not null.
+ */
+ foreach(lc, coalesce->args)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ /* evaluate argument, directly into result datum */
+ ExecInitExprRec(e, parent, state, resv, resnull);
+
+ /* if it's not null, skip to end of COALESCE expr */
+ scratch.opcode = EEOP_JUMP_IF_NOT_NULL;
+ scratch.d.jump.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, &scratch);
+
+ adjust_jumps = lappend_int(adjust_jumps,
+ 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 jump targets */
+ foreach(lc, adjust_jumps)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ Assert(as->opcode == EEOP_JUMP_IF_NOT_NULL);
+ Assert(as->d.jump.jumpdone == -1);
+ as->d.jump.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.
+ */
+
+ /* Perform function lookup */
+ 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 = EEOP_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 = EEOP_SQLVALUEFUNCTION;
+ scratch.d.sqlvaluefunction.svf = svf;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_XmlExpr:
+ {
+ XmlExpr *xexpr = (XmlExpr *) node;
+ int nnamed = list_length(xexpr->named_args);
+ int nargs = list_length(xexpr->args);
+ int off;
+ ListCell *arg;
+
+ scratch.opcode = EEOP_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 = EEOP_NULLTEST_ROWISNULL;
+ else
+ scratch.opcode = EEOP_NULLTEST_ISNULL;
+ }
+ else if (ntest->nulltesttype == IS_NOT_NULL)
+ {
+ if (ntest->argisrow)
+ scratch.opcode = EEOP_NULLTEST_ROWISNOTNULL;
+ else
+ scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
+ }
+ else
+ {
+ elog(ERROR, "unrecognized nulltesttype: %d",
+ (int) ntest->nulltesttype);
+ }
+ /* initialize cache in case it's a row test */
+ scratch.d.nulltest_row.argdesc = NULL;
+
+ /* first evaluate argument into result variable */
+ 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 ok,
+ * because resv/resnull is definitely not used anywhere else,
+ * and will get overwritten by the below EEOP_BOOLTEST_IS_*
+ * step.
+ */
+ ExecInitExprRec(btest->arg, parent, state, resv, resnull);
+
+ switch (btest->booltesttype)
+ {
+ case IS_TRUE:
+ scratch.opcode = EEOP_BOOLTEST_IS_TRUE;
+ break;
+ case IS_NOT_TRUE:
+ scratch.opcode = EEOP_BOOLTEST_IS_NOT_TRUE;
+ break;
+ case IS_FALSE:
+ scratch.opcode = EEOP_BOOLTEST_IS_FALSE;
+ break;
+ case IS_NOT_FALSE:
+ scratch.opcode = EEOP_BOOLTEST_IS_NOT_FALSE;
+ break;
+ case IS_UNKNOWN:
+ /* Same as scalar IS NULL test */
+ scratch.opcode = EEOP_NULLTEST_ISNULL;
+ break;
+ case IS_NOT_UNKNOWN:
+ /* Same as scalar IS NOT NULL test */
+ scratch.opcode = EEOP_NULLTEST_ISNOTNULL;
+ break;
+ default:
+ elog(ERROR, "unrecognized booltesttype: %d",
+ (int) btest->booltesttype);
+ }
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_CoerceToDomain:
+ {
+ CoerceToDomain *ctest = (CoerceToDomain *) node;
+
+ ExecInitCoerceToDomain(&scratch, ctest, parent, state,
+ resv, resnull);
+ break;
+ }
+
+ case T_CoerceToDomainValue:
+ {
+ /*
+ * Read from location identified by innermost_domainval. Note
+ * that innermost_domainval could be NULL, if we're compiling
+ * a standalone domain check rather than one embedded in a
+ * larger expression. In that case we must read from
+ * econtext->domainValue_datum. We'll take care of that
+ * scenario at runtime.
+ */
+ scratch.opcode = EEOP_DOMAIN_TESTVAL;
+ /* we share instruction union variant with case testval */
+ scratch.d.casetest.value = state->innermost_domainval;
+ scratch.d.casetest.isnull = state->innermost_domainnull;
+
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ case T_CurrentOfExpr:
+ {
+ scratch.opcode = EEOP_CURRENTOFEXPR;
+ ExprEvalPushStep(state, &scratch);
+ break;
+ }
+
+ default:
+ elog(ERROR, "unrecognized node type: %d",
+ (int) nodeTag(node));
+ break;
+ }
+}
+
+/*
+ * Add another expression evaluation step to ExprState->steps.
+ *
+ * Note that this potentially re-allocates es->steps, therefore no pointer
+ * into that array may be used while the expression is still being built.
+ */
+static void
+ExprEvalPushStep(ExprState *es, const 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));
+}
+
+/*
+ * Perform setup necessary for the evaluation of a function-like expression,
+ * appending argument evaluation steps to the steps list in *state, and
+ * setting up *scratch so it is ready to be pushed.
+ *
+ * *scratch is not pushed here, so that callers may override the opcode,
+ * which is useful for function-like cases like DISTINCT.
+ */
+static void
+ExecInitFunc(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid,
+ Oid inputcollid, PlanState *parent, ExprState *state)
+{
+ int nargs = list_length(args);
+ AclResult aclresult;
+ FmgrInfo *flinfo;
+ FunctionCallInfo fcinfo;
+ int argno;
+ ListCell *lc;
+
+ /* 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)));
+
+ /* Allocate function lookup data and parameter workspace for this call */
+ scratch->d.func.finfo = palloc0(sizeof(FmgrInfo));
+ scratch->d.func.fcinfo_data = palloc0(sizeof(FunctionCallInfoData));
+ flinfo = scratch->d.func.finfo;
+ fcinfo = scratch->d.func.fcinfo_data;
+
+ /* Set up the primary fmgr lookup information */
+ fmgr_info(funcid, flinfo);
+ fmgr_info_set_expr((Node *) node, flinfo);
+
+ /* Initialize function call parameter structure too */
+ InitFunctionCallInfoData(*fcinfo, flinfo,
+ nargs, inputcollid, NULL, NULL);
+
+ /* Keep extra copies of this info to save an indirection at runtime */
+ scratch->d.func.fn_addr = flinfo->fn_addr;
+ scratch->d.func.nargs = nargs;
+
+ /* We only support non-set functions here */
+ if (flinfo->fn_retset)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ /* Build code to evaluate arguments directly into the fcinfo struct */
+ 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++;
+ }
+
+ /* Insert appropriate opcode depending on strictness and stats level */
+ if (pgstat_track_functions <= flinfo->fn_stats)
+ {
+ if (flinfo->fn_strict && nargs > 0)
+ scratch->opcode = EEOP_FUNCEXPR_STRICT;
+ else
+ scratch->opcode = EEOP_FUNCEXPR;
+ }
+ else
+ {
+ if (flinfo->fn_strict && nargs > 0)
+ scratch->opcode = EEOP_FUNCEXPR_STRICT_FUSAGE;
+ else
+ scratch->opcode = EEOP_FUNCEXPR_FUSAGE;
+ }
+}
+
+/*
+ * Add expression steps deforming the ExprState's inner/outer/scan slots
+ * as much as required by the expression.
+ */
+static void
+ExecInitExprSlots(ExprState *state, Node *node)
+{
+ LastAttnumInfo info = {0, 0, 0};
+ ExprEvalStep scratch;
+
+ /*
+ * Figure out which attributes we're going to need.
+ */
+ get_last_attnums_walker(node, &info);
+
+ /* Emit steps as needed */
+ if (info.last_inner > 0)
+ {
+ scratch.opcode = EEOP_INNER_FETCHSOME;
+ scratch.d.fetch.last_var = info.last_inner;
+ ExprEvalPushStep(state, &scratch);
+ }
+ if (info.last_outer > 0)
+ {
+ scratch.opcode = EEOP_OUTER_FETCHSOME;
+ scratch.d.fetch.last_var = info.last_outer;
+ ExprEvalPushStep(state, &scratch);
+ }
+ if (info.last_scan > 0)
+ {
+ scratch.opcode = EEOP_SCAN_FETCHSOME;
+ scratch.d.fetch.last_var = info.last_scan;
+ ExprEvalPushStep(state, &scratch);
+ }
+}
+
+/*
+ * get_last_attnums_walker: expression walker for ExecInitExprSlots
+ */
+static bool
+get_last_attnums_walker(Node *node, LastAttnumInfo *info)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *variable = (Var *) node;
+ AttrNumber attnum = variable->varattno;
+
+ switch (variable->varno)
+ {
+ case INNER_VAR:
+ info->last_inner = Max(info->last_inner, attnum);
+ break;
+
+ case OUTER_VAR:
+ info->last_outer = Max(info->last_outer, attnum);
+ break;
+
+ /* INDEX_VAR is handled by default case */
+
+ default:
+ info->last_scan = Max(info->last_scan, attnum);
+ break;
+ }
+ return false;
+ }
+
+ /*
+ * Don't examine the arguments or filters of Aggrefs or WindowFuncs,
+ * because those do not represent expressions to be evaluated within the
+ * calling expression's econtext. GroupingFunc arguments are never
+ * evaluated at all.
+ */
+ if (IsA(node, Aggref))
+ return false;
+ if (IsA(node, WindowFunc))
+ return false;
+ if (IsA(node, GroupingFunc))
+ return false;
+ return expression_tree_walker(node, get_last_attnums_walker,
+ (void *) info);
+}
+
+/*
+ * Prepare step for the evaluation of a whole-row variable.
+ * The caller still has to push the step.
+ */
+static void
+ExecInitWholeRowVar(ExprEvalStep *scratch, Var *variable, PlanState *parent)
+{
+ /* fill in all but the target */
+ scratch->opcode = EEOP_WHOLEROW;
+ scratch->d.wholerow.var = variable;
+ scratch->d.wholerow.first = true;
+ scratch->d.wholerow.slow = false;
+ scratch->d.wholerow.tupdesc = NULL; /* filled at runtime */
+ scratch->d.wholerow.junkFilter = NULL;
+
+ /*
+ * 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 so, build the junkfilter now */
+ if (junk_filter_needed)
+ {
+ scratch->d.wholerow.junkFilter =
+ ExecInitJunkFilter(subplan->plan->targetlist,
+ ExecGetResultType(subplan)->tdhasoid,
+ ExecInitExtraTupleSlot(parent->state));
+ }
+ }
+ }
+}
+
+/*
+ * Prepare evaluation of an ArrayRef expression.
+ */
+static void
+ExecInitArrayRef(ExprEvalStep *scratch, ArrayRef *aref, PlanState *parent,
+ ExprState *state, Datum *resv, bool *resnull)
+{
+ bool isAssignment = (aref->refassgnexpr != NULL);
+ ArrayRefState *arefstate = palloc0(sizeof(ArrayRefState));
+ List *adjust_jumps = NIL;
+ ListCell *lc;
+ int i;
+
+ /* Fill constant fields of ArrayRefState */
+ arefstate->isassignment = isAssignment;
+ arefstate->refelemtype = aref->refelemtype;
+ arefstate->refattrlength = get_typlen(aref->refarraytype);
+ get_typlenbyvalalign(aref->refelemtype,
+ &arefstate->refelemlength,
+ &arefstate->refelembyval,
+ &arefstate->refelemalign);
+
+ /*
+ * Evaluate array input. It's safe to do so into resv/resnull, because we
+ * won't use that as target for any of the other subexpressions, and it'll
+ * be overwritten by the final EEOP_ARRAYREF_FETCH/ASSIGN step, which is
+ * pushed last.
+ */
+ ExecInitExprRec(aref->refexpr, parent, state, resv, resnull);
+
+ /*
+ * If refexpr yields NULL, and it's a fetch, then result is NULL. We can
+ * implement this with just JUMP_IF_NULL, since we evaluated the array
+ * into the desired target location.
+ */
+ if (!isAssignment)
+ {
+ scratch->opcode = EEOP_JUMP_IF_NULL;
+ scratch->d.jump.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ }
+
+ /* Verify subscript list lengths are within limit */
+ 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->reflowerindexpr), MAXDIM)));
+
+ /* Evaluate upper subscripts */
+ i = 0;
+ foreach(lc, aref->refupperindexpr)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ /* When slicing, individual subscript bounds can be omitted */
+ if (!e)
+ {
+ arefstate->upperprovided[i] = false;
+ i++;
+ continue;
+ }
+
+ arefstate->upperprovided[i] = true;
+
+ /* Each subscript is evaluated into subscriptvalue/subscriptnull */
+ ExecInitExprRec(e, parent, state,
+ &arefstate->subscriptvalue, &arefstate->subscriptnull);
+
+ /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
+ scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
+ scratch->d.arrayref_subscript.state = arefstate;
+ scratch->d.arrayref_subscript.off = i;
+ scratch->d.arrayref_subscript.isupper = true;
+ scratch->d.arrayref_subscript.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ i++;
+ }
+ arefstate->numupper = i;
+
+ /* Evaluate lower subscripts similarly */
+ i = 0;
+ foreach(lc, aref->reflowerindexpr)
+ {
+ Expr *e = (Expr *) lfirst(lc);
+
+ /* When slicing, individual subscript bounds can be omitted */
+ if (!e)
+ {
+ arefstate->lowerprovided[i] = false;
+ i++;
+ continue;
+ }
+
+ arefstate->lowerprovided[i] = true;
+
+ /* Each subscript is evaluated into subscriptvalue/subscriptnull */
+ ExecInitExprRec(e, parent, state,
+ &arefstate->subscriptvalue, &arefstate->subscriptnull);
+
+ /* ... and then ARRAYREF_SUBSCRIPT saves it into step's workspace */
+ scratch->opcode = EEOP_ARRAYREF_SUBSCRIPT;
+ scratch->d.arrayref_subscript.state = arefstate;
+ scratch->d.arrayref_subscript.off = i;
+ scratch->d.arrayref_subscript.isupper = false;
+ scratch->d.arrayref_subscript.jumpdone = -1; /* adjust later */
+ ExprEvalPushStep(state, scratch);
+ adjust_jumps = lappend_int(adjust_jumps,
+ state->steps_len - 1);
+ i++;
+ }
+ arefstate->numlower = i;
+
+ /* Should be impossible if parser is sane, but check anyway: */
+ if (arefstate->numlower != 0 &&
+ arefstate->numupper != arefstate->numlower)
+ elog(ERROR, "upper and lower index lists are not same length");
+
+ if (isAssignment)
+ {
+ Datum *save_innermost_caseval;
+ bool *save_innermost_casenull;
+
+ /*
+ * 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 CaseTextExpr mechanism. 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
+ * innermost_caseval 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 = EEOP_ARRAYREF_OLD;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ /* ARRAYREF_OLD puts extracted value into prevvalue/prevnull */
+ 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 into replacevalue/replacenull */
+ ExecInitExprRec(aref->refassgnexpr, parent, state,
+ &arefstate->replacevalue, &arefstate->replacenull);
+
+ state->innermost_caseval = save_innermost_caseval;
+ state->innermost_casenull = save_innermost_casenull;
+
+ /* and perform the assignment */
+ scratch->opcode = EEOP_ARRAYREF_ASSIGN;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+ else
+ {
+ /* array fetch is much simpler */
+ scratch->opcode = EEOP_ARRAYREF_FETCH;
+ scratch->d.arrayref.state = arefstate;
+ ExprEvalPushStep(state, scratch);
+ }
+
+ /* adjust jump targets */
+ foreach(lc, adjust_jumps)
+ {
+ ExprEvalStep *as = &state->steps[lfirst_int(lc)];
+
+ if (as->opcode == EEOP_ARRAYREF_SUBSCRIPT)
+ {
+ Assert(as->d.arrayref_subscript.jumpdone == -1);
+ as->d.arrayref_subscript.jumpdone = state->steps_len;
+ }
+ else
+ {
+ Assert(as->opcode == EEOP_JUMP_IF_NULL);
+ Assert(as->d.jump.jumpdone == -1);
+ as->d.jump.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;
+}
+
+/*
+ * Prepare evaluation of a CoerceToDomain expression.
+ */
+static void
+ExecInitCoerceToDomain(ExprEvalStep *scratch, CoerceToDomain *ctest,
+ PlanState *parent, ExprState *state,
+ Datum *resv, bool *resnull)
+{
+ ExprEvalStep scratch2;
+ DomainConstraintRef *constraint_ref;
+ Datum *domainval = NULL;
+ bool *domainnull = NULL;
+ Datum *save_innermost_domainval;
+ bool *save_innermost_domainnull;
+ ListCell *l;
+
+ scratch->d.domaincheck.resulttype = ctest->resulttype;
+ /* we'll allocate workspace only if needed */
+ scratch->d.domaincheck.checkvalue = NULL;
+ scratch->d.domaincheck.checknull = NULL;
+
+ /*
+ * Evaluate argument - it's fine to directly store it into resv/resnull,
+ * if there's constraint failures there'll be errors, otherwise it's what
+ * needs to be returned.
+ */
+ ExecInitExprRec(ctest->arg, parent, state, resv, resnull);
+
+ /*
+ * Note: if the argument is of varlena type, it could be a R/W expanded
+ * object. We want to return the R/W pointer as the final result, but we
+ * have to pass a R/O pointer as the value to be tested by any functions
+ * in check expressions. We don't bother to emit a MAKE_READONLY step
+ * unless there's actually at least one check expression, though. Until
+ * we've tested that, domainval/domainnull are NULL.
+ */
+
+ /*
+ * Collect the constraints associated with the domain.
+ *
+ * Note: before PG v10 we'd recheck the set of constraints during each
+ * evaluation of the expression. Now we bake them into the ExprState
+ * during executor initialization. That means we don't need typcache.c to
+ * provide compiled exprs.
+ */
+ constraint_ref = (DomainConstraintRef *)
+ palloc(sizeof(DomainConstraintRef));
+ InitDomainConstraintRef(ctest->resulttype,
+ constraint_ref,
+ CurrentMemoryContext,
+ false);
+
+ /*
+ * Compile code to check each domain constraint. NOTNULL constraints can
+ * just be applied on the resv/resnull value, but for CHECK constraints we
+ * need more pushups.
+ */
+ 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 = EEOP_DOMAIN_NOTNULL;
+ ExprEvalPushStep(state, scratch);
+ break;
+ case DOM_CONSTRAINT_CHECK:
+ /* Allocate workspace for CHECK output if we didn't yet */
+ if (scratch->d.domaincheck.checkvalue == NULL)
+ {
+ scratch->d.domaincheck.checkvalue =
+ (Datum *) palloc(sizeof(Datum));
+ scratch->d.domaincheck.checknull =
+ (bool *) palloc(sizeof(bool));
+ }
+
+ /*
+ * If first time through, determine where CoerceToDomainValue
+ * nodes should read from.
+ */
+ if (domainval == NULL)
+ {
+ /*
+ * Since value might be read multiple times, force to R/O
+ * - but only if it could be an expanded datum.
+ */
+ if (get_typlen(ctest->resulttype) == -1)
+ {
+ /* Yes, so make output workspace for MAKE_READONLY */
+ domainval = (Datum *) palloc(sizeof(Datum));
+ domainnull = (bool *) palloc(sizeof(bool));
+
+ /* Emit MAKE_READONLY */
+ scratch2.opcode = EEOP_MAKE_READONLY;
+ scratch2.resvalue = domainval;
+ scratch2.resnull = domainnull;
+ scratch2.d.make_readonly.value = resv;
+ scratch2.d.make_readonly.isnull = resnull;
+ ExprEvalPushStep(state, &scratch2);
+ }
+ else
+ {
+ /* No, so it's fine to read from resv/resnull */
+ domainval = resv;
+ domainnull = resnull;
+ }
+ }
+
+ /*
+ * 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 = domainval;
+ state->innermost_domainnull = domainnull;
+
+ /* evaluate check expression value */
+ ExecInitExprRec(con->check_expr, parent, state,
+ scratch->d.domaincheck.checkvalue,
+ scratch->d.domaincheck.checknull);
+
+ state->innermost_domainval = save_innermost_domainval;
+ state->innermost_domainnull = save_innermost_domainnull;
+
+ /* now test result */
+ scratch->opcode = EEOP_DOMAIN_CHECK;
+ ExprEvalPushStep(state, scratch);
+
+ break;
+ default:
+ elog(ERROR, "unrecognized constraint type: %d",
+ (int) con->constrainttype);
+ break;
+ }
+ }
+}
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * execExprInterp.c
+ * Interpreted evaluation of an expression step list.
+ *
+ * This file provides either a "direct threaded" (for gcc, clang and
+ * compatible) or a "switch threaded" (for all compilers) implementation of
+ * expression evaluation. The former is amongst the fastest known methods
+ * of interpreting programs without resorting to assembly level work, or
+ * just-in-time compilation, but it requires support for computed gotos.
+ * The latter is amongst the fastest approaches doable in standard C.
+ *
+ * In either case we use ExprEvalStep->opcode to dispatch to the code block
+ * within ExecInterpExpr() that implements the specific opcode type.
+ *
+ * Switch-threading uses a plain switch() statement to perform the
+ * dispatch. This has the advantages of being plain C and allowing the
+ * compiler 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, requiring more jumps and causing
+ * bad branch prediction.
+ *
+ * In direct threading, we use gcc's label-as-values extension - also adopted
+ * by some other compilers - to replace ExprEvalStep->opcode with the address
+ * of the block implementing the instruction. Dispatch to the next instruction
+ * is done by a "computed goto". This allows for better branch prediction
+ * (as the jumps are happening from different locations) and fewer jumps
+ * (as no preparatory jump to a common dispatch location is needed).
+ *
+ * When using direct threading, ExecReadyInterpretedExpr will replace
+ * each step's opcode field with the address of the relevant code block and
+ * ExprState->flags will contain EEO_FLAG_DIRECT_THREADED to remember that
+ * that's been done.
+ *
+ * For very simple instructions the overhead of the full interpreter
+ * "startup", as minimal as it is, is noticeable. Therefore
+ * ExecReadyInterpretedExpr will choose to implement simple scalar Var
+ * and Const expressions using special fast-path routines (ExecJust*).
+ * Benchmarking shows anything more complex than those may as well use the
+ * "full interpreter".
+ *
+ * Complex or uncommon instructions are not implemented in-line in
+ * ExecInterpExpr(), rather we call out to a helper function appearing later
+ * in this file. For one reason, there'd not be a noticeable performance
+ * benefit, but more importantly those complex routines are intended to be
+ * shared between different expression evaluation approaches. For instance
+ * a JIT compiler would generate calls to them. (This is why they are
+ * exported rather than being "static" in this file.)
+ *
+ *
+ * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ *
+ * IDENTIFICATION
+ * src/backend/executor/execExprInterp.c
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/htup_details.h"
+#include "catalog/pg_type.h"
+#include "executor/execExpr.h"
+#include "executor/nodeSubplan.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 use a separate symbol so that it's easy to adjust locally in this file
+ * for development and testing.
+ */
+#ifdef HAVE_COMPUTED_GOTO
+#define EEO_USE_COMPUTED_GOTO
+#endif /* HAVE_COMPUTED_GOTO */
+
+/*
+ * Macros for opcode dispatch.
+ *
+ * EEO_SWITCH - just hides the switch if not in use.
+ * EEO_CASE - labels the implementation of named expression step type.
+ * EEO_DISPATCH - jump to the implementation of the step type for 'op'.
+ * EEO_OPCODE - compute opcode required by used expression evaluation method.
+ * EEO_NEXT - increment 'op' and jump to correct next step type.
+ * EEO_JUMP - jump to the specified step number within the current expression.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+/* to make dispatch_table accessible outside ExecInterpExpr() */
+static const void **dispatch_table = NULL;
+
+#define EEO_SWITCH()
+#define EEO_CASE(name) CASE_##name:
+#define EEO_DISPATCH() goto *((void *) op->opcode)
+#define EEO_OPCODE(opcode) ((intptr_t) dispatch_table[opcode])
+
+#else /* !EEO_USE_COMPUTED_GOTO */
+
+#define EEO_SWITCH() starteval: switch ((ExprEvalOp) op->opcode)
+#define EEO_CASE(name) case name:
+#define EEO_DISPATCH() goto starteval
+#define EEO_OPCODE(opcode) (opcode)
+
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+#define EEO_NEXT() \
+ do { \
+ op++; \
+ EEO_DISPATCH(); \
+ } while (0)
+
+#define EEO_JUMP(stepno) \
+ do { \
+ op = &state->steps[stepno]; \
+ EEO_DISPATCH(); \
+ } while (0)
+
+
+static Datum ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull);
+static void ExecInitInterpreter(void);
+
+/* support functions */
+static void CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype);
+static TupleDesc get_cached_rowtype(Oid type_id, int32 typmod,
+ TupleDesc *cache_field, ExprContext *econtext);
+static void ShutdownTupleDescRef(Datum arg);
+static void ExecEvalRowNullInt(ExprState *state, ExprEvalStep *op,
+ ExprContext *econtext, bool checkisnull);
+
+/* fast-path evaluation functions */
+static Datum ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustScanVarFirst(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 ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull);
+static Datum ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull);
+
+
+/*
+ * Prepare ExprState for interpreted execution.
+ */
+void
+ExecReadyInterpretedExpr(ExprState *state)
+{
+ /* Ensure one-time interpreter setup has been done */
+ ExecInitInterpreter();
+
+ /* Simple validity checks on expression */
+ Assert(state->steps_len >= 1);
+ Assert(state->steps[state->steps_len - 1].opcode == EEOP_DONE);
+
+ /*
+ * Don't perform redundant initialization. This is unreachable in current
+ * cases, but might be hit if there's additional expression evaluation
+ * methods that rely on interpreted execution to work.
+ */
+ if (state->flags & EEO_FLAG_INTERPRETER_INITIALIZED)
+ return;
+
+ /* DIRECT_THREADED should not already be set */
+ Assert((state->flags & EEO_FLAG_DIRECT_THREADED) == 0);
+
+ /*
+ * There shouldn't be any errors before the expression is fully
+ * initialized, and even if so, it'd lead to the expression being
+ * abandoned. So we can set the flag now and save some code.
+ */
+ state->flags |= EEO_FLAG_INTERPRETER_INITIALIZED;
+
+ /*
+ * Select fast-path evalfuncs 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 always.
+ */
+ if (state->steps_len == 3)
+ {
+ ExprEvalOp step0 = state->steps[0].opcode;
+ ExprEvalOp step1 = state->steps[1].opcode;
+
+ if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_INNER_VAR_FIRST)
+ {
+ state->evalfunc = ExecJustInnerVarFirst;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_OUTER_VAR_FIRST)
+ {
+ state->evalfunc = ExecJustOuterVarFirst;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_SCAN_VAR_FIRST)
+ {
+ state->evalfunc = ExecJustScanVarFirst;
+ return;
+ }
+ else if (step0 == EEOP_INNER_FETCHSOME &&
+ step1 == EEOP_ASSIGN_INNER_VAR)
+ {
+ state->evalfunc = ExecJustAssignInnerVar;
+ return;
+ }
+ else if (step0 == EEOP_OUTER_FETCHSOME &&
+ step1 == EEOP_ASSIGN_OUTER_VAR)
+ {
+ state->evalfunc = ExecJustAssignOuterVar;
+ return;
+ }
+ else if (step0 == EEOP_SCAN_FETCHSOME &&
+ step1 == EEOP_ASSIGN_SCAN_VAR)
+ {
+ state->evalfunc = ExecJustAssignScanVar;
+ return;
+ }
+ }
+ else if (state->steps_len == 2 &&
+ state->steps[0].opcode == EEOP_CONST)
+ {
+ state->evalfunc = ExecJustConst;
+ return;
+ }
+
+#if defined(EEO_USE_COMPUTED_GOTO)
+
+ /*
+ * In the direct-threaded implementation, replace each opcode with the
+ * address to jump to. (Use ExecEvalStepOp() to get back the opcode.)
+ */
+ {
+ int off;
+
+ for (off = 0; off < state->steps_len; off++)
+ {
+ ExprEvalStep *op = &state->steps[off];
+
+ op->opcode = EEO_OPCODE(op->opcode);
+ }
+
+ state->flags |= EEO_FLAG_DIRECT_THREADED;
+ }
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+ state->evalfunc = ExecInterpExpr;
+}
+
+
+/*
+ * Evaluate expression identified by "state" in the execution context
+ * given by "econtext". *isnull is set to the is-null flag for the result,
+ * and the Datum value is the function result.
+ *
+ * As a special case, return the dispatch table's address if state is NULL.
+ * This is used by ExecInitInterpreter to set up the dispatch_table global.
+ * (Only applies when EEO_USE_COMPUTED_GOTO is defined.)
+ */
+static Datum
+ExecInterpExpr(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op;
+ TupleTableSlot *resultslot;
+ TupleTableSlot *innerslot;
+ TupleTableSlot *outerslot;
+ TupleTableSlot *scanslot;
+
+ /*
+ * This array has to be in the same order as enum ExprEvalOp.
+ */
+#if defined(EEO_USE_COMPUTED_GOTO)
+ static const void *const dispatch_table[] = {
+ &&CASE_EEOP_DONE,
+ &&CASE_EEOP_INNER_FETCHSOME,
+ &&CASE_EEOP_OUTER_FETCHSOME,
+ &&CASE_EEOP_SCAN_FETCHSOME,
+ &&CASE_EEOP_INNER_VAR_FIRST,
+ &&CASE_EEOP_INNER_VAR,
+ &&CASE_EEOP_OUTER_VAR_FIRST,
+ &&CASE_EEOP_OUTER_VAR,
+ &&CASE_EEOP_SCAN_VAR_FIRST,
+ &&CASE_EEOP_SCAN_VAR,
+ &&CASE_EEOP_INNER_SYSVAR,
+ &&CASE_EEOP_OUTER_SYSVAR,
+ &&CASE_EEOP_SCAN_SYSVAR,
+ &&CASE_EEOP_WHOLEROW,
+ &&CASE_EEOP_ASSIGN_INNER_VAR,
+ &&CASE_EEOP_ASSIGN_OUTER_VAR,
+ &&CASE_EEOP_ASSIGN_SCAN_VAR,
+ &&CASE_EEOP_ASSIGN_TMP,
+ &&CASE_EEOP_ASSIGN_TMP_MAKE_RO,
+ &&CASE_EEOP_CONST,
+ &&CASE_EEOP_FUNCEXPR,
+ &&CASE_EEOP_FUNCEXPR_STRICT,
+ &&CASE_EEOP_FUNCEXPR_FUSAGE,
+ &&CASE_EEOP_FUNCEXPR_STRICT_FUSAGE,
+ &&CASE_EEOP_BOOL_AND_STEP_FIRST,
+ &&CASE_EEOP_BOOL_AND_STEP,
+ &&CASE_EEOP_BOOL_AND_STEP_LAST,
+ &&CASE_EEOP_BOOL_OR_STEP_FIRST,
+ &&CASE_EEOP_BOOL_OR_STEP,
+ &&CASE_EEOP_BOOL_OR_STEP_LAST,
+ &&CASE_EEOP_BOOL_NOT_STEP,
+ &&CASE_EEOP_QUAL,
+ &&CASE_EEOP_JUMP,
+ &&CASE_EEOP_JUMP_IF_NULL,
+ &&CASE_EEOP_JUMP_IF_NOT_NULL,
+ &&CASE_EEOP_JUMP_IF_NOT_TRUE,
+ &&CASE_EEOP_NULLTEST_ISNULL,
+ &&CASE_EEOP_NULLTEST_ISNOTNULL,
+ &&CASE_EEOP_NULLTEST_ROWISNULL,
+ &&CASE_EEOP_NULLTEST_ROWISNOTNULL,
+ &&CASE_EEOP_BOOLTEST_IS_TRUE,
+ &&CASE_EEOP_BOOLTEST_IS_NOT_TRUE,
+ &&CASE_EEOP_BOOLTEST_IS_FALSE,
+ &&CASE_EEOP_BOOLTEST_IS_NOT_FALSE,
+ &&CASE_EEOP_PARAM_EXEC,
+ &&CASE_EEOP_PARAM_EXTERN,
+ &&CASE_EEOP_CASE_TESTVAL,
+ &&CASE_EEOP_MAKE_READONLY,
+ &&CASE_EEOP_IOCOERCE,
+ &&CASE_EEOP_DISTINCT,
+ &&CASE_EEOP_NULLIF,
+ &&CASE_EEOP_SQLVALUEFUNCTION,
+ &&CASE_EEOP_CURRENTOFEXPR,
+ &&CASE_EEOP_ARRAYEXPR,
+ &&CASE_EEOP_ARRAYCOERCE,
+ &&CASE_EEOP_ROW,
+ &&CASE_EEOP_ROWCOMPARE_STEP,
+ &&CASE_EEOP_ROWCOMPARE_FINAL,
+ &&CASE_EEOP_MINMAX,
+ &&CASE_EEOP_FIELDSELECT,
+ &&CASE_EEOP_FIELDSTORE_DEFORM,
+ &&CASE_EEOP_FIELDSTORE_FORM,
+ &&CASE_EEOP_ARRAYREF_SUBSCRIPT,
+ &&CASE_EEOP_ARRAYREF_OLD,
+ &&CASE_EEOP_ARRAYREF_ASSIGN,
+ &&CASE_EEOP_ARRAYREF_FETCH,
+ &&CASE_EEOP_DOMAIN_TESTVAL,
+ &&CASE_EEOP_DOMAIN_NOTNULL,
+ &&CASE_EEOP_DOMAIN_CHECK,
+ &&CASE_EEOP_CONVERT_ROWTYPE,
+ &&CASE_EEOP_SCALARARRAYOP,
+ &&CASE_EEOP_XMLEXPR,
+ &&CASE_EEOP_AGGREF,
+ &&CASE_EEOP_GROUPING_FUNC,
+ &&CASE_EEOP_WINDOW_FUNC,
+ &&CASE_EEOP_SUBPLAN,
+ &&CASE_EEOP_ALTERNATIVE_SUBPLAN,
+ &&CASE_EEOP_LAST
+ };
+
+ StaticAssertStmt(EEOP_LAST + 1 == lengthof(dispatch_table),
+ "dispatch_table out of whack with ExprEvalOp");
+
+ if (unlikely(state == NULL))
+ return PointerGetDatum(dispatch_table);
+#else
+ Assert(state != NULL);
+#endif /* EEO_USE_COMPUTED_GOTO */
+
+ /* 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();
+#endif
+
+ EEO_SWITCH()
+ {
+ EEO_CASE(EEOP_DONE)
+ {
+ goto out;
+ }
+
+ EEO_CASE(EEOP_INNER_FETCHSOME)
+ {
+ /* XXX: worthwhile to check tts_nvalid inline first? */
+ slot_getsomeattrs(innerslot, op->d.fetch.last_var);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_FETCHSOME)
+ {
+ slot_getsomeattrs(outerslot, op->d.fetch.last_var);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_FETCHSOME)
+ {
+ slot_getsomeattrs(scanslot, op->d.fetch.last_var);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_VAR_FIRST)
+ {
+ int attnum = op->d.var.attnum;
+
+ /*
+ * First time through, check whether attribute matches Var. Might
+ * not be ok anymore, due to schema changes.
+ */
+ CheckVarSlotCompatibility(innerslot, attnum + 1, op->d.var.vartype);
+
+ /* Skip that check on subsequent evaluations */
+ op->opcode = EEO_OPCODE(EEOP_INNER_VAR);
+
+ /* FALL THROUGH to EEOP_INNER_VAR */
+ }
+
+ EEO_CASE(EEOP_INNER_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /*
+ * Since we already extracted all referenced columns from the
+ * tuple with a FETCHSOME step, we can just grab the value
+ * directly out of the slot's decomposed-data arrays. But let's
+ * have an Assert to check that that did happen.
+ */
+ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
+ *op->resvalue = innerslot->tts_values[attnum];
+ *op->resnull = innerslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR_FIRST)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEOP_INNER_VAR_FIRST comments */
+
+ CheckVarSlotCompatibility(outerslot, attnum + 1, op->d.var.vartype);
+ op->opcode = EEO_OPCODE(EEOP_OUTER_VAR);
+
+ /* FALL THROUGH to EEOP_OUTER_VAR */
+ }
+
+ EEO_CASE(EEOP_OUTER_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEOP_INNER_VAR comments */
+
+ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
+ *op->resvalue = outerslot->tts_values[attnum];
+ *op->resnull = outerslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR_FIRST)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEOP_INNER_VAR_FIRST comments */
+
+ CheckVarSlotCompatibility(scanslot, attnum + 1, op->d.var.vartype);
+ op->opcode = EEO_OPCODE(EEOP_SCAN_VAR);
+
+ /* FALL THROUGH to EEOP_SCAN_VAR */
+ }
+
+ EEO_CASE(EEOP_SCAN_VAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* See EEOP_INNER_VAR comments */
+
+ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
+ *op->resvalue = scanslot->tts_values[attnum];
+ *op->resnull = scanslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_INNER_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* these asserts must match defenses in slot_getattr */
+ Assert(innerslot->tts_tuple != NULL);
+ Assert(innerslot->tts_tuple != &(innerslot->tts_minhdr));
+ /* heap_getsysattr has sufficient defenses against bad attnums */
+
+ *op->resvalue = heap_getsysattr(innerslot->tts_tuple, attnum,
+ innerslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_OUTER_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* these asserts must match defenses in slot_getattr */
+ Assert(outerslot->tts_tuple != NULL);
+ Assert(outerslot->tts_tuple != &(outerslot->tts_minhdr));
+
+ /* heap_getsysattr has sufficient defenses against bad attnums */
+ *op->resvalue = heap_getsysattr(outerslot->tts_tuple, attnum,
+ outerslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCAN_SYSVAR)
+ {
+ int attnum = op->d.var.attnum;
+
+ /* these asserts must match defenses in slot_getattr */
+ Assert(scanslot->tts_tuple != NULL);
+ Assert(scanslot->tts_tuple != &(scanslot->tts_minhdr));
+ /* heap_getsysattr has sufficient defenses against bad attnums */
+
+ *op->resvalue = heap_getsysattr(scanslot->tts_tuple, attnum,
+ scanslot->tts_tupleDescriptor,
+ op->resnull);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_WHOLEROW)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalWholeRowVar(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ASSIGN_INNER_VAR)
+ {
+ int resultnum = op->d.assign_var.resultnum;
+ int attnum = op->d.assign_var.attnum;
+
+ /*
+ * We do not need CheckVarSlotCompatibility here; that was taken
+ * care of at compilation time. But see EEOP_INNER_VAR comments.
+ */
+ Assert(attnum >= 0 && attnum < innerslot->tts_nvalid);
+ resultslot->tts_values[resultnum] = innerslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = innerslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ASSIGN_OUTER_VAR)
+ {
+ int resultnum = op->d.assign_var.resultnum;
+ int attnum = op->d.assign_var.attnum;
+
+ /*
+ * We do not need CheckVarSlotCompatibility here; that was taken
+ * care of at compilation time. But see EEOP_INNER_VAR comments.
+ */
+ Assert(attnum >= 0 && attnum < outerslot->tts_nvalid);
+ resultslot->tts_values[resultnum] = outerslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = outerslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ASSIGN_SCAN_VAR)
+ {
+ int resultnum = op->d.assign_var.resultnum;
+ int attnum = op->d.assign_var.attnum;
+
+ /*
+ * We do not need CheckVarSlotCompatibility here; that was taken
+ * care of at compilation time. But see EEOP_INNER_VAR comments.
+ */
+ Assert(attnum >= 0 && attnum < scanslot->tts_nvalid);
+ resultslot->tts_values[resultnum] = scanslot->tts_values[attnum];
+ resultslot->tts_isnull[resultnum] = scanslot->tts_isnull[attnum];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ASSIGN_TMP)
+ {
+ int resultnum = op->d.assign_tmp.resultnum;
+
+ resultslot->tts_values[resultnum] = state->resvalue;
+ resultslot->tts_isnull[resultnum] = state->resnull;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ASSIGN_TMP_MAKE_RO)
+ {
+ int 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_NEXT();
+ }
+
+ EEO_CASE(EEOP_CONST)
+ {
+ *op->resnull = op->d.constval.isnull;
+ *op->resvalue = op->d.constval.value;
+
+ EEO_NEXT();
+ }
+
+ /*
+ * 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!), it's worth having so many separate opcodes.
+ */
+ EEO_CASE(EEOP_FUNCEXPR)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.func.fn_addr) (fcinfo);
+ *op->resnull = fcinfo->isnull;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_FUNCEXPR_STRICT)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ /* strict function, so 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_NEXT();
+ }
+
+ EEO_CASE(EEOP_FUNCEXPR_FUSAGE)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+
+ 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_NEXT();
+ }
+
+ EEO_CASE(EEOP_FUNCEXPR_STRICT_FUSAGE)
+ {
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+ PgStat_FunctionCallUsage fcusage;
+ bool *argnull = fcinfo->argnull;
+ int argno;
+
+ /* strict function, so 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;
+
+ pgstat_end_function_usage(&fcusage, true);
+
+ strictfail_fusage:
+ EEO_NEXT();
+ }
+
+ /*
+ * If any of its clauses is FALSE, an AND's 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": perhaps one of the "don't
+ * knows" would have been FALSE if we'd known its value. Only when
+ * all the inputs are known to be TRUE can we state confidently that
+ * the AND's result is TRUE.
+ */
+ EEO_CASE(EEOP_BOOL_AND_STEP_FIRST)
+ {
+ *op->d.boolexpr.anynull = false;
+
+ /*
+ * EEOP_BOOL_AND_STEP_FIRST resets anynull, otherwise it's the
+ * same as EEOP_BOOL_AND_STEP - so fall through to that.
+ */
+
+ /* FALL THROUGH */
+ }
+
+ EEO_CASE(EEOP_BOOL_AND_STEP)
+ {
+ if (*op->resnull)
+ {
+ *op->d.boolexpr.anynull = true;
+ }
+ else if (!DatumGetBool(*op->resvalue))
+ {
+ /* result is already set to FALSE, need not change it */
+ /* bail out early */
+ EEO_JUMP(op->d.boolexpr.jumpdone);
+ }
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOL_AND_STEP_LAST)
+ {
+ if (*op->resnull)
+ {
+ /* result is already set to NULL, need not change it */
+ }
+ else if (!DatumGetBool(*op->resvalue))
+ {
+ /* result is already set to FALSE, need not change it */
+
+ /*
+ * 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->resvalue = (Datum) 0;
+ *op->resnull = true;
+ }
+ else
+ {
+ /* result is already set to TRUE, need not change it */
+ }
+
+ EEO_NEXT();
+ }
+
+ /*
+ * If any of its clauses is TRUE, an OR's 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": 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(EEOP_BOOL_OR_STEP_FIRST)
+ {
+ *op->d.boolexpr.anynull = false;
+
+ /*
+ * EEOP_BOOL_OR_STEP_FIRST resets anynull, otherwise it's the same
+ * as EEOP_BOOL_OR_STEP - so fall through to that.
+ */
+
+ /* FALL THROUGH */
+ }
+
+ EEO_CASE(EEOP_BOOL_OR_STEP)
+ {
+ if (*op->resnull)
+ {
+ *op->d.boolexpr.anynull = true;
+ }
+ else if (DatumGetBool(*op->resvalue))
+ {
+ /* result is already set to TRUE, need not change it */
+ /* bail out early */
+ EEO_JUMP(op->d.boolexpr.jumpdone);
+ }
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOL_OR_STEP_LAST)
+ {
+ if (*op->resnull)
+ {
+ /* result is already set to NULL, need not change it */
+ }
+ else if (DatumGetBool(*op->resvalue))
+ {
+ /* result is already set to TRUE, need not change it */
+
+ /*
+ * 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->resvalue = (Datum) 0;
+ *op->resnull = true;
+ }
+ else
+ {
+ /* result is already set to FALSE, need not change it */
+ }
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOL_NOT_STEP)
+ {
+ /*
+ * Evaluation of 'not' is simple... if expr is false, then return
+ * 'true' and vice versa. It's safe to do this even on a
+ * nominally null value, so we ignore resnull; that means that
+ * NULL in produces NULL out, which is what we want.
+ */
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_QUAL)
+ {
+ /* simplified version of BOOL_AND_STEP for use by ExecQual() */
+
+ /* If argument (also result) is false or null ... */
+ if (*op->resnull ||
+ !DatumGetBool(*op->resvalue))
+ {
+ /* ... bail out early, returning FALSE */
+ *op->resnull = false;
+ *op->resvalue = BoolGetDatum(false);
+ EEO_JUMP(op->d.qualexpr.jumpdone);
+ }
+
+ /*
+ * Otherwise, leave the TRUE value in place, in case this is the
+ * last qual. Then, TRUE is the correct answer.
+ */
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_JUMP)
+ {
+ /* Unconditionally jump to target step */
+ EEO_JUMP(op->d.jump.jumpdone);
+ }
+
+ EEO_CASE(EEOP_JUMP_IF_NULL)
+ {
+ /* Transfer control if current result is null */
+ if (*op->resnull)
+ EEO_JUMP(op->d.jump.jumpdone);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_JUMP_IF_NOT_NULL)
+ {
+ /* Transfer control if current result is non-null */
+ if (!*op->resnull)
+ EEO_JUMP(op->d.jump.jumpdone);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_JUMP_IF_NOT_TRUE)
+ {
+ /* Transfer control if current result is null or false */
+ if (*op->resnull || !DatumGetBool(*op->resvalue))
+ EEO_JUMP(op->d.jump.jumpdone);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_NULLTEST_ISNULL)
+ {
+ *op->resvalue = BoolGetDatum(*op->resnull);
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_NULLTEST_ISNOTNULL)
+ {
+ *op->resvalue = BoolGetDatum(!*op->resnull);
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_NULLTEST_ROWISNULL)
+ {
+ /* out of line implementation: too large */
+ ExecEvalRowNull(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_NULLTEST_ROWISNOTNULL)
+ {
+ /* out of line implementation: too large */
+ ExecEvalRowNotNull(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ /* BooleanTest implementations for all booltesttypes */
+
+ EEO_CASE(EEOP_BOOLTEST_IS_TRUE)
+ {
+ if (*op->resnull)
+ *op->resvalue = BoolGetDatum(false);
+ else
+ *op->resvalue = *op->resvalue;
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOLTEST_IS_NOT_TRUE)
+ {
+ if (*op->resnull)
+ *op->resvalue = BoolGetDatum(true);
+ else
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOLTEST_IS_FALSE)
+ {
+ if (*op->resnull)
+ *op->resvalue = BoolGetDatum(false);
+ else
+ *op->resvalue = BoolGetDatum(!DatumGetBool(*op->resvalue));
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_BOOLTEST_IS_NOT_FALSE)
+ {
+ if (*op->resnull)
+ *op->resvalue = BoolGetDatum(true);
+ else
+ *op->resvalue = *op->resvalue;
+ *op->resnull = false;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_PARAM_EXEC)
+ {
+ /* out of line implementation: too large */
+ ExecEvalParamExec(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_PARAM_EXTERN)
+ {
+ /* out of line implementation: too large */
+ ExecEvalParamExtern(state, op, econtext);
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CASE_TESTVAL)
+ {
+ /*
+ * Normally upper parts of 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 enough to
+ * worry about an extra 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_NEXT();
+ }
+
+ EEO_CASE(EEOP_DOMAIN_TESTVAL)
+ {
+ /*
+ * See EEOP_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_NEXT();
+ }
+
+ EEO_CASE(EEOP_MAKE_READONLY)
+ {
+ /*
+ * Force a varlena value that might be read multiple times to R/O
+ */
+ if (!*op->d.make_readonly.isnull)
+ *op->resvalue =
+ MakeExpandedObjectReadOnlyInternal(*op->d.make_readonly.value);
+ *op->resnull = *op->d.make_readonly.isnull;
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_IOCOERCE)
+ {
+ /*
+ * Evaluate a CoerceViaIO node. This can be quite a hot path, so
+ * inline as much work as possible. The source value is in our
+ * result variable.
+ */
+ 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;
+
+ fcinfo_out->isnull = false;
+ str = DatumGetCString(FunctionCallInvoke(fcinfo_out));
+
+ /* OutputFunctionCall assumes result isn't null */
+ 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] = *op->resnull;
+ /* second and third arguments are already set up */
+
+ 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_NEXT();
+ }
+
+ EEO_CASE(EEOP_DISTINCT)
+ {
+ /*
+ * 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 determined. If neither is
+ * NULL, then proceed to evaluate the comparison function, which
+ * is just the type's standard equality operator. We need not
+ * care whether that function is strict. Because the handling of
+ * nulls is different, we can't just reuse EEOP_FUNCEXPR.
+ */
+ 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->resvalue = BoolGetDatum(false);
+ *op->resnull = false;
+ }
+ else if (fcinfo->argnull[0] || fcinfo->argnull[1])
+ {
+ /* Only one is NULL? Then is distinct... */
+ *op->resvalue = BoolGetDatum(true);
+ *op->resnull = false;
+ }
+ else
+ {
+ /* Neither null, so apply the equality function */
+ Datum eqresult;
+
+ fcinfo->isnull = false;
+ eqresult = (op->d.func.fn_addr) (fcinfo);
+ /* Must invert result of "="; safe to do even if null */
+ *op->resvalue = BoolGetDatum(!DatumGetBool(eqresult));
+ *op->resnull = fcinfo->isnull;
+ }
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_NULLIF)
+ {
+ /*
+ * The arguments are already evaluated into fcinfo->arg/argnull.
+ */
+ FunctionCallInfo fcinfo = op->d.func.fcinfo_data;
+
+ /* if either argument is NULL they can't be equal */
+ if (!fcinfo->argnull[0] && !fcinfo->argnull[1])
+ {
+ Datum result;
+
+ fcinfo->isnull = false;
+ result = (op->d.func.fn_addr) (fcinfo);
+
+ /* if the arguments are equal return null */
+ if (!fcinfo->isnull && DatumGetBool(result))
+ {
+ *op->resvalue = (Datum) 0;
+ *op->resnull = true;
+
+ EEO_NEXT();
+ }
+ }
+
+ /* Arguments aren't equal, so return the first one */
+ *op->resvalue = fcinfo->arg[0];
+ *op->resnull = fcinfo->argnull[0];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SQLVALUEFUNCTION)
+ {
+ /*
+ * Doesn't seem worthwhile to have an inline implementation
+ * efficiency-wise.
+ */
+ ExecEvalSQLValueFunction(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CURRENTOFEXPR)
+ {
+ /* error invocation uses space, and shouldn't ever occur */
+ ExecEvalCurrentOfExpr(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ARRAYEXPR)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayExpr(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ARRAYCOERCE)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayCoerce(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ROW)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalRow(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ROWCOMPARE_STEP)
+ {
+ FunctionCallInfo fcinfo = op->d.rowcompare_step.fcinfo_data;
+
+ /* force NULL result if strict fn and NULL input */
+ if (op->d.rowcompare_step.finfo->fn_strict &&
+ (fcinfo->argnull[0] || fcinfo->argnull[1]))
+ {
+ *op->resnull = true;
+ EEO_JUMP(op->d.rowcompare_step.jumpnull);
+ }
+
+ /* Apply comparison function */
+ fcinfo->isnull = false;
+ *op->resvalue = (op->d.rowcompare_step.fn_addr) (fcinfo);
+
+ /* force NULL result if NULL function result */
+ if (fcinfo->isnull)
+ {
+ *op->resnull = true;
+ EEO_JUMP(op->d.rowcompare_step.jumpnull);
+ }
+ *op->resnull = false;
+
+ /* If unequal, no need to compare remaining columns */
+ if (DatumGetInt32(*op->resvalue) != 0)
+ {
+ EEO_JUMP(op->d.rowcompare_step.jumpdone);
+ }
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_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_NEXT();
+ }
+
+ EEO_CASE(EEOP_MINMAX)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalMinMax(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_FIELDSELECT)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldSelect(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_FIELDSTORE_DEFORM)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldStoreDeForm(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_FIELDSTORE_FORM)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalFieldStoreForm(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ARRAYREF_SUBSCRIPT)
+ {
+ /* Process an array subscript */
+
+ /* too complex for an inline implementation */
+ if (ExecEvalArrayRefSubscript(state, op))
+ {
+ EEO_NEXT();
+ }
+ else
+ {
+ /* Subscript is null, short-circuit ArrayRef to NULL */
+ EEO_JUMP(op->d.arrayref_subscript.jumpdone);
+ }
+ }
+
+ EEO_CASE(EEOP_ARRAYREF_OLD)
+ {
+ /*
+ * Fetch the old value in an arrayref assignment, in case it's
+ * referenced (via a CaseTestExpr) inside the assignment
+ * expression.
+ */
+
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefOld(state, op);
+
+ EEO_NEXT();
+ }
+
+ /*
+ * Perform ArrayRef assignment
+ */
+ EEO_CASE(EEOP_ARRAYREF_ASSIGN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefAssign(state, op);
+
+ EEO_NEXT();
+ }
+
+ /*
+ * Fetch subset of an array.
+ */
+ EEO_CASE(EEOP_ARRAYREF_FETCH)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalArrayRefFetch(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_CONVERT_ROWTYPE)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConvertRowtype(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SCALARARRAYOP)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalScalarArrayOp(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_DOMAIN_NOTNULL)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConstraintNotNull(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_DOMAIN_CHECK)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalConstraintCheck(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_XMLEXPR)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalXmlExpr(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_AGGREF)
+ {
+ /*
+ * Returns a Datum whose value is the precomputed aggregate value
+ * found in the given expression context.
+ */
+ AggrefExprState *aggref = op->d.aggref.astate;
+
+ Assert(econtext->ecxt_aggvalues != NULL);
+
+ *op->resvalue = econtext->ecxt_aggvalues[aggref->aggno];
+ *op->resnull = econtext->ecxt_aggnulls[aggref->aggno];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_GROUPING_FUNC)
+ {
+ /* too complex/uncommon for an inline implementation */
+ ExecEvalGroupingFunc(state, op);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_WINDOW_FUNC)
+ {
+ /*
+ * Like Aggref, just return a precomputed value from the econtext.
+ */
+ WindowFuncExprState *wfunc = op->d.window_func.wfstate;
+
+ Assert(econtext->ecxt_aggvalues != NULL);
+
+ *op->resvalue = econtext->ecxt_aggvalues[wfunc->wfuncno];
+ *op->resnull = econtext->ecxt_aggnulls[wfunc->wfuncno];
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_SUBPLAN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalSubPlan(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_ALTERNATIVE_SUBPLAN)
+ {
+ /* too complex for an inline implementation */
+ ExecEvalAlternativeSubPlan(state, op, econtext);
+
+ EEO_NEXT();
+ }
+
+ EEO_CASE(EEOP_LAST)
+ {
+ /* unreachable */
+ Assert(false);
+ goto out;
+ }
+ }
+
+out:
+ *isnull = state->resnull;
+ return state->resvalue;
+}
+
+/*
+ * Check whether a user attribute in a slot can be referenced by a Var
+ * expression. This should succeed unless there have been schema changes
+ * since the expression tree has been created.
+ */
+static void
+CheckVarSlotCompatibility(TupleTableSlot *slot, int attnum, Oid vartype)
+{
+ /*
+ * 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.
+ *
+ * System attributes don't require checking since their types never
+ * change.
+ *
+ * Note: we allow a reference to a dropped attribute. slot_getattr will
+ * force a NULL result in such cases.
+ *
+ * Note: ideally we'd check typmod as well as typid, but that seems
+ * impractical at the moment: in many cases the tupdesc will have been
+ * generated by ExecTypeFromTL(), and that can't guarantee to generate an
+ * accurate typmod in all cases, because some expression node types don't
+ * carry typmod.
+ */
+ if (attnum > 0)
+ {
+ TupleDesc slot_tupdesc = slot->tts_tupleDescriptor;
+ Form_pg_attribute attr;
+
+ if (attnum > slot_tupdesc->natts) /* should never happen */
+ elog(ERROR, "attribute number %d exceeds number of columns %d",
+ attnum, slot_tupdesc->natts);
+
+ attr = slot_tupdesc->attrs[attnum - 1];
+
+ /* can't check type if dropped, since atttypid is probably 0 */
+ if (!attr->attisdropped)
+ {
+ if (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(vartype))));
+ }
+ }
+}
+
+/*
+ * 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 econtext shutdown
+ */
+static void
+ShutdownTupleDescRef(Datum arg)
+{
+ TupleDesc *cache_field = (TupleDesc *) DatumGetPointer(arg);
+
+ if (*cache_field)
+ ReleaseTupleDesc(*cache_field);
+ *cache_field = NULL;
+}
+
+/*
+ * Fast-path functions, for very simple expressions
+ */
+
+/* Simple reference to inner Var, first time through */
+static Datum
+ExecJustInnerVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_innertuple;
+
+ /* See ExecInterpExpr()'s comments for EEOP_INNER_VAR_FIRST */
+
+ CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+ op->opcode = EEOP_INNER_VAR; /* just for cleanliness */
+ state->evalfunc = ExecJustInnerVar;
+
+ /*
+ * Since we use slot_getattr(), we don't need to implement the FETCHSOME
+ * step explicitly, and we also needn't Assert that the attnum is in range
+ * --- slot_getattr() will take care of any problems.
+ */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to inner Var */
+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;
+
+ /* See comments in ExecJustInnerVarFirst */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to outer Var, first time through */
+static Datum
+ExecJustOuterVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_outertuple;
+
+ CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+ op->opcode = EEOP_OUTER_VAR; /* just for cleanliness */
+ state->evalfunc = ExecJustOuterVar;
+
+ /* See comments in ExecJustInnerVarFirst */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to outer Var */
+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;
+
+ /* See comments in ExecJustInnerVarFirst */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to scan Var, first time through */
+static Datum
+ExecJustScanVarFirst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.var.attnum + 1;
+ TupleTableSlot *slot = econtext->ecxt_scantuple;
+
+ CheckVarSlotCompatibility(slot, attnum, op->d.var.vartype);
+ op->opcode = EEOP_SCAN_VAR; /* just for cleanliness */
+ state->evalfunc = ExecJustScanVar;
+
+ /* See comments in ExecJustInnerVarFirst */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple reference to scan Var */
+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;
+
+ /* See comments in ExecJustInnerVarFirst */
+ return slot_getattr(slot, attnum, isnull);
+}
+
+/* Simple Const expression */
+static Datum
+ExecJustConst(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[0];
+
+ *isnull = op->d.constval.isnull;
+ return op->d.constval.value;
+}
+
+/* Evaluate inner Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignInnerVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ int resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_innertuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ /*
+ * We do not need CheckVarSlotCompatibility here; that was taken care of
+ * at compilation time.
+ *
+ * Since we use slot_getattr(), we don't need to implement the FETCHSOME
+ * step explicitly, and we also needn't Assert that the attnum is in range
+ * --- slot_getattr() will take care of any problems.
+ */
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+/* Evaluate outer Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignOuterVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ int resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_outertuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ /* See comments in ExecJustAssignInnerVar */
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+/* Evaluate scan Var and assign to appropriate column of result tuple */
+static Datum
+ExecJustAssignScanVar(ExprState *state, ExprContext *econtext, bool *isnull)
+{
+ ExprEvalStep *op = &state->steps[1];
+ int attnum = op->d.assign_var.attnum + 1;
+ int resultnum = op->d.assign_var.resultnum;
+ TupleTableSlot *inslot = econtext->ecxt_scantuple;
+ TupleTableSlot *outslot = state->resultslot;
+
+ /* See comments in ExecJustAssignInnerVar */
+ outslot->tts_values[resultnum] =
+ slot_getattr(inslot, attnum, &outslot->tts_isnull[resultnum]);
+ return 0;
+}
+
+
+/*
+ * Do one-time initialization of interpretation machinery.
+ */
+static void
+ExecInitInterpreter(void)
+{
+#if defined(EEO_USE_COMPUTED_GOTO)
+ /* Set up externally-visible pointer to dispatch table */
+ if (dispatch_table == NULL)
+ dispatch_table = (const void **)
+ DatumGetPointer(ExecInterpExpr(NULL, NULL, NULL));
+#endif
+}
+
+/*
+ * Function to return the opcode of an expression step.
+ *
+ * When direct-threading is in use, ExprState->opcode isn't easily
+ * decipherable. This function returns the appropriate enum member.
+ *
+ * This currently is only supposed to be used in paths&