Faster expression evaluation and targetlist projection.

This replaces the old, recursive tree-walk based evaluation, with
non-recursive, opcode dispatch based, expression evaluation.
Projection is now implemented as part of expression evaluation.

This both leads to significant performance improvements, and makes
future just-in-time compilation of expressions easier.

The speed gains primarily come from:
- non-recursive implementation reduces stack usage / overhead
- simple sub-expressions are implemented with a single jump, without
  function calls
- sharing some state between different sub-expressions
- reduced amount of indirect/hard to predict memory accesses by laying
  out operation metadata sequentially; including the avoidance of
  nearly all of the previously used linked lists
- more code has been moved to expression initialization, avoiding
  constant re-checks at evaluation time

Future just-in-time compilation (JIT) has become easier, as
demonstrated by released patches intended to be merged in a later
release, for primarily two reasons: Firstly, due to a stricter split
between expression initialization and evaluation, less code has to be
handled by the JIT. Secondly, due to the non-recursive nature of the
generated "instructions", less performance-critical code-paths can
easily be shared between interpreted and compiled evaluation.

The new framework allows for significant future optimizations. E.g.:
- basic infrastructure for to later reduce the per executor-startup
  overhead of expression evaluation, by caching state in prepared
  statements.  That'd be helpful in OLTPish scenarios where
  initialization overhead is measurable.
- optimizing the generated "code". A number of proposals for potential
  work has already been made.
- optimizing the interpreter. Similarly a number of proposals have
  been made here too.

The move of logic into the expression initialization step leads to some
backward-incompatible changes:
- Function permission checks are now done during expression
  initialization, whereas previously they were done during
  execution. In edge cases this can lead to errors being raised that
  previously wouldn't have been, e.g. a NULL array being coerced to a
  different array type previously didn't perform checks.
- The set of domain constraints to be checked, is now evaluated once
  during expression initialization, previously it was re-built
  every time a domain check was evaluated. For normal queries this
  doesn't change much, but e.g. for plpgsql functions, which caches
  ExprStates, the old set could stick around longer.  The behavior
  around might still change.

Author: Andres Freund, with significant changes by Tom Lane,
	changes by Heikki Linnakangas
Reviewed-By: Tom Lane, Heikki Linnakangas
Discussion: https://postgr.es/m/20161206034955.bh33paeralxbtluv@alap3.anarazel.de
This commit is contained in:
Andres Freund 2017-03-14 15:45:36 -07:00
parent 7d3957e53e
commit b8d7f053c5
71 changed files with 8868 additions and 6531 deletions

View File

@ -3421,7 +3421,7 @@ prepare_query_params(PlanState *node,
* benefit, and it'd require postgres_fdw to know more than is desirable
* about Param evaluation.)
*/
*param_exprs = (List *) ExecInitExpr((Expr *) fdw_exprs, node);
*param_exprs = ExecInitExprList(fdw_exprs, node);
/* Allocate buffer for text form of query parameters. */
*param_values = (const char **) palloc0(numParams * sizeof(char *));

View File

@ -1084,7 +1084,7 @@ index_register(Oid heap,
/* predicate will likely be null, but may as well copy it */
newind->il_info->ii_Predicate = (List *)
copyObject(indexInfo->ii_Predicate);
newind->il_info->ii_PredicateState = NIL;
newind->il_info->ii_PredicateState = NULL;
/* no exclusion constraints at bootstrap time, so no need to copy */
Assert(indexInfo->ii_ExclusionOps == NULL);
Assert(indexInfo->ii_ExclusionProcs == NULL);

View File

@ -1658,7 +1658,7 @@ BuildIndexInfo(Relation index)
/* fetch index predicate if any */
ii->ii_Predicate = RelationGetIndexPredicate(index);
ii->ii_PredicateState = NIL;
ii->ii_PredicateState = NULL;
/* fetch exclusion constraint info if any */
if (indexStruct->indisexclusion)
@ -1774,9 +1774,8 @@ FormIndexDatum(IndexInfo *indexInfo,
indexInfo->ii_ExpressionsState == NIL)
{
/* First time through, set up expression evaluation state */
indexInfo->ii_ExpressionsState = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Expressions,
estate);
indexInfo->ii_ExpressionsState =
ExecPrepareExprList(indexInfo->ii_Expressions, estate);
/* Check caller has set up context correctly */
Assert(GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
}
@ -2208,7 +2207,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
double reltuples;
List *predicate;
ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@ -2247,9 +2246,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Prepare for scan of the base relation. In a normal index build, we use
@ -2552,9 +2549,9 @@ IndexBuildHeapRangeScan(Relation heapRelation,
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
if (predicate != NIL)
if (predicate != NULL)
{
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}
@ -2619,7 +2616,7 @@ IndexBuildHeapRangeScan(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
return reltuples;
}
@ -2646,7 +2643,7 @@ IndexCheckExclusion(Relation heapRelation,
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
List *predicate;
ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@ -2672,9 +2669,7 @@ IndexCheckExclusion(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Scan all live tuples in the base relation.
@ -2699,9 +2694,9 @@ IndexCheckExclusion(Relation heapRelation,
/*
* In a partial index, ignore tuples that don't satisfy the predicate.
*/
if (predicate != NIL)
if (predicate != NULL)
{
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}
@ -2732,7 +2727,7 @@ IndexCheckExclusion(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
}
@ -2962,7 +2957,7 @@ validate_index_heapscan(Relation heapRelation,
HeapTuple heapTuple;
Datum values[INDEX_MAX_KEYS];
bool isnull[INDEX_MAX_KEYS];
List *predicate;
ExprState *predicate;
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
@ -2992,9 +2987,7 @@ validate_index_heapscan(Relation heapRelation,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate, if any. */
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/*
* Prepare for scan of the base relation. We need just those tuples
@ -3121,9 +3114,9 @@ validate_index_heapscan(Relation heapRelation,
* In a partial index, discard tuples that don't satisfy the
* predicate.
*/
if (predicate != NIL)
if (predicate != NULL)
{
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}
@ -3177,7 +3170,7 @@ validate_index_heapscan(Relation heapRelation,
/* These may have been pointing to the now-gone estate */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
}

View File

@ -1618,8 +1618,7 @@ FormPartitionKeyDatum(PartitionDispatch pd,
GetPerTupleExprContext(estate)->ecxt_scantuple == slot);
/* First time through, set up expression evaluation state */
pd->keystate = (List *) ExecPrepareExpr((Expr *) pd->key->partexprs,
estate);
pd->keystate = ExecPrepareExprList(pd->key->partexprs, estate);
}
partexpr_item = list_head(pd->keystate);

View File

@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = NIL;
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;

View File

@ -713,7 +713,7 @@ compute_index_stats(Relation onerel, double totalrows,
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
List *predicate;
ExprState *predicate;
Datum *exprvals;
bool *exprnulls;
int numindexrows,
@ -739,9 +739,7 @@ compute_index_stats(Relation onerel, double totalrows,
econtext->ecxt_scantuple = slot;
/* Set up execution state for predicate. */
predicate = castNode(List,
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate));
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
/* Compute and save index expression values */
exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum));
@ -764,9 +762,9 @@ compute_index_stats(Relation onerel, double totalrows,
ExecStoreTuple(heapTuple, slot, InvalidBuffer, false);
/* If index is partial, check predicate */
if (predicate != NIL)
if (predicate != NULL)
{
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}
numindexrows++;

View File

@ -2890,7 +2890,7 @@ ExplainSubPlans(List *plans, List *ancestors,
foreach(lst, plans)
{
SubPlanState *sps = (SubPlanState *) lfirst(lst);
SubPlan *sp = (SubPlan *) sps->xprstate.expr;
SubPlan *sp = sps->subplan;
/*
* There can be multiple SubPlan nodes referencing the same physical

View File

@ -179,7 +179,7 @@ CheckIndexCompatible(Oid oldId,
indexInfo = makeNode(IndexInfo);
indexInfo->ii_Expressions = NIL;
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;
@ -551,7 +551,7 @@ DefineIndex(Oid relationId,
indexInfo->ii_Expressions = NIL; /* for now */
indexInfo->ii_ExpressionsState = NIL;
indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause);
indexInfo->ii_PredicateState = NIL;
indexInfo->ii_PredicateState = NULL;
indexInfo->ii_ExclusionOps = NULL;
indexInfo->ii_ExclusionProcs = NULL;
indexInfo->ii_ExclusionStrats = NULL;

View File

@ -391,7 +391,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
}
/* Prepare the expressions for execution */
exprstates = (List *) ExecPrepareExpr((Expr *) params, estate);
exprstates = ExecPrepareExprList(params, estate);
paramLI = (ParamListInfo)
palloc(offsetof(ParamListInfoData, params) +
@ -407,7 +407,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
i = 0;
foreach(l, exprstates)
{
ExprState *n = lfirst(l);
ExprState *n = (ExprState *) lfirst(l);
ParamExternData *prm = &paramLI->params[i];
prm->ptype = param_types[i];

View File

@ -185,7 +185,7 @@ typedef struct NewConstraint
Oid refindid; /* OID of PK's index, if FOREIGN */
Oid conid; /* OID of pg_constraint entry, if FOREIGN */
Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */
List *qualstate; /* Execution state for CHECK */
ExprState *qualstate; /* Execution state for CHECK expr */
} NewConstraint;
/*
@ -4262,7 +4262,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
CommandId mycid;
BulkInsertState bistate;
int hi_options;
List *partqualstate = NIL;
ExprState *partqualstate = NULL;
/*
* Open the relation(s). We have surely already locked the existing
@ -4315,8 +4315,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
{
case CONSTR_CHECK:
needscan = true;
con->qualstate = (List *)
ExecPrepareExpr((Expr *) con->qual, estate);
con->qualstate = ExecPrepareExpr((Expr *) con->qual, estate);
break;
case CONSTR_FOREIGN:
/* Nothing to do here */
@ -4331,9 +4330,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
if (tab->partition_constraint)
{
needscan = true;
partqualstate = (List *)
ExecPrepareExpr((Expr *) tab->partition_constraint,
estate);
partqualstate = ExecPrepareCheck(tab->partition_constraint, estate);
}
foreach(l, tab->newvals)
@ -4508,7 +4505,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
switch (con->contype)
{
case CONSTR_CHECK:
if (!ExecQual(con->qualstate, econtext, true))
if (!ExecCheck(con->qualstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",
@ -4524,7 +4521,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
}
}
if (partqualstate && !ExecQual(partqualstate, econtext, true))
if (partqualstate && !ExecCheck(partqualstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("partition constraint is violated by some row")));
@ -6607,8 +6604,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
newcon = (NewConstraint *) palloc0(sizeof(NewConstraint));
newcon->name = ccon->name;
newcon->contype = ccon->contype;
/* ExecQual wants implicit-AND format */
newcon->qual = (Node *) make_ands_implicit((Expr *) ccon->expr);
newcon->qual = ccon->expr;
tab->constraints = lappend(tab->constraints, newcon);
}
@ -7786,7 +7782,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
Datum val;
char *conbin;
Expr *origexpr;
List *exprstate;
ExprState *exprstate;
TupleDesc tupdesc;
HeapScanDesc scan;
HeapTuple tuple;
@ -7817,8 +7813,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
HeapTupleGetOid(constrtup));
conbin = TextDatumGetCString(val);
origexpr = (Expr *) stringToNode(conbin);
exprstate = (List *)
ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate);
exprstate = ExecPrepareExpr(origexpr, estate);
econtext = GetPerTupleExprContext(estate);
tupdesc = RelationGetDescr(rel);
@ -7838,7 +7833,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup)
{
ExecStoreTuple(tuple, slot, InvalidBuffer, false);
if (!ExecQual(exprstate, econtext, true))
if (!ExecCheck(exprstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("check constraint \"%s\" is violated by some row",

View File

@ -3057,7 +3057,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
if (trigger->tgqual)
{
TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc);
List **predicate;
ExprState **predicate;
ExprContext *econtext;
TupleTableSlot *oldslot = NULL;
TupleTableSlot *newslot = NULL;
@ -3078,7 +3078,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
* nodetrees for it. Keep them in the per-query memory context so
* they'll survive throughout the query.
*/
if (*predicate == NIL)
if (*predicate == NULL)
{
Node *tgqual;
@ -3087,9 +3087,9 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
/* Change references to OLD and NEW to INNER_VAR and OUTER_VAR */
ChangeVarNodes(tgqual, PRS2_OLD_VARNO, INNER_VAR, 0);
ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0);
/* ExecQual wants implicit-AND form */
/* ExecPrepareQual wants implicit-AND form */
tgqual = (Node *) make_ands_implicit((Expr *) tgqual);
*predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate);
*predicate = ExecPrepareQual((List *) tgqual, estate);
MemoryContextSwitchTo(oldContext);
}
@ -3137,7 +3137,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo,
*/
econtext->ecxt_innertuple = oldslot;
econtext->ecxt_outertuple = newslot;
if (!ExecQual(*predicate, econtext, false))
if (!ExecQual(*predicate, econtext))
return false;
}

View File

@ -12,9 +12,10 @@ subdir = src/backend/executor
top_builddir = ../../..
include $(top_builddir)/src/Makefile.global
OBJS = execAmi.o execCurrent.o execGrouping.o execIndexing.o execJunk.o \
execMain.o execParallel.o execProcnode.o execQual.o \
execReplication.o execScan.o execTuples.o \
OBJS = execAmi.o execCurrent.o execExpr.o execExprInterp.o \
execGrouping.o execIndexing.o execJunk.o \
execMain.o execParallel.o execProcnode.o \
execReplication.o execScan.o execSRF.o execTuples.o \
execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \
nodeBitmapAnd.o nodeBitmapOr.o \
nodeBitmapHeapscan.o nodeBitmapIndexscan.o \

View File

@ -44,21 +44,171 @@ Plan Trees and State Trees
--------------------------
The plan tree delivered by the planner contains a tree of Plan nodes (struct
types derived from struct Plan). Each Plan node may have expression trees
associated with it, to represent its target list, qualification conditions,
etc. During executor startup we build a parallel tree of identical structure
containing executor state nodes --- every plan and expression node type has
a corresponding executor state node type. Each node in the state tree has a
pointer to its corresponding node in the plan tree, plus executor state data
as needed to implement that node type. This arrangement allows the plan
tree to be completely read-only as far as the executor is concerned: all data
that is modified during execution is in the state tree. Read-only plan trees
make life much simpler for plan caching and reuse.
types derived from struct Plan). During executor startup we build a parallel
tree of identical structure containing executor state nodes --- every plan
node type has a corresponding executor state node type. Each node in the
state tree has a pointer to its corresponding node in the plan tree, plus
executor state data as needed to implement that node type. This arrangement
allows the plan tree to be completely read-only so far as the executor is
concerned: all data that is modified during execution is in the state tree.
Read-only plan trees make life much simpler for plan caching and reuse.
Each Plan node may have expression trees associated with it, to represent
its target list, qualification conditions, etc. These trees are also
read-only to the executor, but the executor state for expression evaluation
does not mirror the Plan expression's tree shape, as explained below.
Rather, there's just one ExprState node per expression tree, although this
may have sub-nodes for some complex expression node types.
Altogether there are four classes of nodes used in these trees: Plan nodes,
their corresponding PlanState nodes, Expr nodes, and their corresponding
ExprState nodes. (Actually, there are also List nodes, which are used as
"glue" in all four kinds of tree.)
their corresponding PlanState nodes, Expr nodes, and ExprState nodes.
(Actually, there are also List nodes, which are used as "glue" in all
three tree-based representations.)
Expression Trees and ExprState nodes
------------------------------------
Expression trees, in contrast to Plan trees, are not mirrored into a
corresponding tree of state nodes. Instead each separately executable
expression tree (e.g. a Plan's qual or targetlist) is represented by one
ExprState node. The ExprState node contains the information needed to
evaluate the expression in a compact, linear form. That compact form is
stored as a flat array in ExprState->steps[] (an array of ExprEvalStep,
not ExprEvalStep *).
The reasons for choosing such a representation include:
- commonly the amount of work needed to evaluate one Expr-type node is
small enough that the overhead of having to perform a tree-walk
during evaluation is significant.
- the flat representation can be evaluated non-recursively within a single
function, reducing stack depth and function call overhead.
- such a representation is usable both for fast interpreted execution,
and for compiling into native code.
The Plan-tree representation of an expression is compiled into an
ExprState node by ExecInitExpr(). As much complexity as possible should
be handled by ExecInitExpr() (and helpers), instead of execution time
where both interpreted and compiled versions would need to deal with the
complexity. Besides duplicating effort between execution approaches,
runtime initialization checks also have a small but noticeable cost every
time the expression is evaluated. Therefore, we allow ExecInitExpr() to
precompute information that we do not expect to vary across execution of a
single query, for example the set of CHECK constraint expressions to be
applied to a domain type. This could not be done at plan time without
greatly increasing the number of events that require plan invalidation.
(Previously, some information of this kind was rechecked on each
expression evaluation, but that seems like unnecessary overhead.)
Expression Initialization
-------------------------
During ExecInitExpr() and similar routines, Expr trees are converted
into the flat representation. Each Expr node might be represented by
zero, one, or more ExprEvalSteps.
Each ExprEvalStep's work is determined by its opcode (of enum ExprEvalOp)
and it stores the result of its work into the Datum variable and boolean
null flag variable pointed to by ExprEvalStep->resvalue/resnull.
Complex expressions are performed by chaining together several steps.
For example, "a + b" (one OpExpr, with two Var expressions) would be
represented as two steps to fetch the Var values, and one step for the
evaluation of the function underlying the + operator. The steps for the
Vars would have their resvalue/resnull pointing directly to the appropriate
arg[] and argnull[] array elements in the FunctionCallInfoData struct that
is used by the function evaluation step, thus avoiding extra work to copy
the result values around.
The last entry in a completed ExprState->steps array is always an
EEOP_DONE step; this removes the need to test for end-of-array while
iterating. Also, if the expression contains any variable references (to
user columns of the ExprContext's INNER, OUTER, or SCAN tuples), the steps
array begins with EEOP_*_FETCHSOME steps that ensure that the relevant
tuples have been deconstructed to make the required columns directly
available (cf. slot_getsomeattrs()). This allows individual Var-fetching
steps to be little more than an array lookup.
Most of ExecInitExpr()'s work is done by the recursive function
ExecInitExprRec() and its subroutines. ExecInitExprRec() maps one Expr
node into the steps required for execution, recursing as needed for
sub-expressions.
Each ExecInitExprRec() call has to specify where that subexpression's
results are to be stored (via the resv/resnull parameters). This allows
the above scenario of evaluating a (sub-)expression directly into
fcinfo->arg/argnull, but also requires some care: target Datum/isnull
variables may not be shared with another ExecInitExprRec() unless the
results are only needed by steps executing before further usages of those
target Datum/isnull variables. Due to the non-recursiveness of the
ExprEvalStep representation that's usually easy to guarantee.
ExecInitExprRec() pushes new operations into the ExprState->steps array
using ExprEvalPushStep(). To keep the steps as a consecutively laid out
array, ExprEvalPushStep() has to repalloc the entire array when there's
not enough space. Because of that it is *not* allowed to point directly
into any of the steps during expression initialization. Therefore, the
resv/resnull for a subexpression usually point to some storage that is
palloc'd separately from the steps array. For instance, the
FunctionCallInfoData for a function call step is separately allocated
rather than being part of the ExprEvalStep array. The overall result
of a complete expression is typically returned into the resvalue/resnull
fields of the ExprState node itself.
Some steps, e.g. boolean expressions, allow skipping evaluation of
certain subexpressions. In the flat representation this amounts to
jumping to some later step rather than just continuing consecutively
with the next step. The target for such a jump is represented by
the integer index in the ExprState->steps array of the step to execute
next. (Compare the EEO_NEXT and EEO_JUMP macros in execExprInterp.c.)
Typically, ExecInitExprRec() has to push a jumping step into the steps
array, then recursively generate steps for the subexpression that might
get skipped over, then go back and fix up the jump target index using
the now-known length of the subexpression's steps. This is handled by
adjust_jumps lists in execExpr.c.
The last step in constructing an ExprState is to apply ExecReadyExpr(),
which readies it for execution using whichever execution method has been
selected.
Expression Evaluation
---------------------
To allow for different methods of expression evaluation, and for
better branch/jump target prediction, expressions are evaluated by
calling ExprState->evalfunc (via ExprEvalExpr() and friends).
ExprReadyExpr() can choose the method of interpretation by setting
evalfunc to an appropriate function. The default execution function,
ExecInterpExpr, is implemented in execExprInterp.c; see its header
comment for details. Special-case evalfuncs are used for certain
especially-simple expressions.
Note that a lot of the more complex expression evaluation steps, which are
less performance-critical than the simpler ones, are implemented as
separate functions outside the fast-path of expression execution, allowing
their implementation to be shared between interpreted and compiled
expression evaluation. This means that these helper functions are not
allowed to perform expression step dispatch themselves, as the method of
dispatch will vary based on the caller. The helpers therefore cannot call
for the execution of subexpressions; all subexpression results they need
must be computed by earlier steps. And dispatch to the following
expression step must be performed after returning from the helper.
Targetlist Evaluation
---------------------
ExecBuildProjectionInfo builds an ExprState that has the effect of
evaluating a targetlist into ExprState->resultslot. A generic targetlist
expression is executed by evaluating it as discussed above (storing the
result into the ExprState's resvalue/resnull fields) and then using an
EEOP_ASSIGN_TMP step to move the result into the appropriate tts_values[]
and tts_isnull[] array elements of the result slot. There are special
fast-path step types (EEOP_ASSIGN_*_VAR) to handle targetlist entries that
are simple Vars using only one step instead of two.
Memory Management

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot,
/* Check for partial index */
if (indexInfo->ii_Predicate != NIL)
{
List *predicate;
ExprState *predicate;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
if (predicate == NIL)
if (predicate == NULL)
{
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
indexInfo->ii_PredicateState = predicate;
}
/* Skip this index-update if the predicate isn't satisfied */
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}
@ -551,23 +549,21 @@ ExecCheckIndexConstraints(TupleTableSlot *slot,
/* Check for partial index */
if (indexInfo->ii_Predicate != NIL)
{
List *predicate;
ExprState *predicate;
/*
* If predicate state not set up yet, create it (in the estate's
* per-query context)
*/
predicate = indexInfo->ii_PredicateState;
if (predicate == NIL)
if (predicate == NULL)
{
predicate = (List *)
ExecPrepareExpr((Expr *) indexInfo->ii_Predicate,
estate);
predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate);
indexInfo->ii_PredicateState = predicate;
}
/* Skip this index-update if the predicate isn't satisfied */
if (!ExecQual(predicate, econtext, false))
if (!ExecQual(predicate, econtext))
continue;
}

View File

@ -600,8 +600,8 @@ ExecCheckRTEPerms(RangeTblEntry *rte)
/*
* Only plain-relation RTEs need to be checked here. Function RTEs are
* checked by init_fcache when the function is prepared for execution.
* Join, subquery, and special RTEs need no checks.
* checked when the function is prepared for execution. Join, subquery,
* and special RTEs need no checks.
*/
if (rte->rtekind != RTE_RELATION)
return true;
@ -1275,8 +1275,8 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo,
resultRelInfo->ri_TrigFunctions = (FmgrInfo *)
palloc0(n * sizeof(FmgrInfo));
resultRelInfo->ri_TrigWhenExprs = (List **)
palloc0(n * sizeof(List *));
resultRelInfo->ri_TrigWhenExprs = (ExprState **)
palloc0(n * sizeof(ExprState *));
if (instrument_options)
resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options);
}
@ -1723,7 +1723,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
ConstrCheck *check = rel->rd_att->constr->check;
ExprContext *econtext;
MemoryContext oldContext;
List *qual;
int i;
/*
@ -1735,13 +1734,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
{
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
resultRelInfo->ri_ConstraintExprs =
(List **) palloc(ncheck * sizeof(List *));
(ExprState **) palloc(ncheck * sizeof(ExprState *));
for (i = 0; i < ncheck; i++)
{
/* ExecQual wants implicit-AND form */
qual = make_ands_implicit(stringToNode(check[i].ccbin));
resultRelInfo->ri_ConstraintExprs[i] = (List *)
ExecPrepareExpr((Expr *) qual, estate);
Expr *checkconstr;
checkconstr = stringToNode(check[i].ccbin);
resultRelInfo->ri_ConstraintExprs[i] =
ExecPrepareExpr(checkconstr, estate);
}
MemoryContextSwitchTo(oldContext);
}
@ -1758,14 +1758,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo,
/* And evaluate the constraints */
for (i = 0; i < ncheck; i++)
{
qual = resultRelInfo->ri_ConstraintExprs[i];
ExprState *checkconstr = resultRelInfo->ri_ConstraintExprs[i];
/*
* NOTE: SQL specifies that a NULL result from a constraint expression
* is not to be treated as a failure. Therefore, tell ExecQual to
* return TRUE for NULL.
* is not to be treated as a failure. Therefore, use ExecCheck not
* ExecQual.
*/
if (!ExecQual(qual, econtext, true))
if (!ExecCheck(checkconstr, econtext))
return check[i].ccname;
}
@ -1793,8 +1793,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
{
List *qual = resultRelInfo->ri_PartitionCheck;
resultRelInfo->ri_PartitionCheckExpr = (List *)
ExecPrepareExpr((Expr *) qual, estate);
resultRelInfo->ri_PartitionCheckExpr = ExecPrepareCheck(qual, estate);
}
/*
@ -1810,7 +1809,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot,
* As in case of the catalogued constraints, we treat a NULL result as
* success here, not a failure.
*/
return ExecQual(resultRelInfo->ri_PartitionCheckExpr, econtext, true);
return ExecCheck(resultRelInfo->ri_PartitionCheckExpr, econtext);
}
/*
@ -1990,11 +1989,9 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo,
* is visible (in the case of a view) or that it passes the
* 'with-check' policy (in the case of row security). If the qual
* evaluates to NULL or FALSE, then the new tuple won't be included in
* the view or doesn't pass the 'with-check' policy for the table. We
* need ExecQual to return FALSE for NULL to handle the view case (the
* opposite of what we do above for CHECK constraints).
* the view or doesn't pass the 'with-check' policy for the table.
*/
if (!ExecQual((List *) wcoExpr, econtext, false))
if (!ExecQual(wcoExpr, econtext))
{
char *val_desc;
Bitmapset *modifiedCols;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,924 @@
/*-------------------------------------------------------------------------
*
* execSRF.c
* Routines implementing the API for set-returning functions
*
* This file serves nodeFunctionscan.c and nodeProjectSet.c, providing
* common code for calling set-returning functions according to the
* ReturnSetInfo API.
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/backend/executor/execSRF.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/htup_details.h"
#include "catalog/objectaccess.h"
#include "executor/execdebug.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
/* static function decls */
static void init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF);
static void ShutdownSetExpr(Datum arg);
static void ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList, ExprContext *econtext);
static void ExecPrepareTuplestoreResult(SetExprState *sexpr,
ExprContext *econtext,
Tuplestorestate *resultStore,
TupleDesc resultDesc);
static void tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc);
/*
* Prepare function call in FROM (ROWS FROM) for execution.
*
* This is used by nodeFunctionscan.c.
*/
SetExprState *
ExecInitTableFunctionResult(Expr *expr,
ExprContext *econtext, PlanState *parent)
{
SetExprState *state = makeNode(SetExprState);
state->funcReturnsSet = false;
state->expr = expr;
state->func.fn_oid = InvalidOid;
/*
* Normally the passed expression tree will be a FuncExpr, since the
* grammar only allows a function call at the top level of a table
* function reference. However, if the function doesn't return set then
* the planner might have replaced the function call via constant-folding
* or inlining. So if we see any other kind of expression node, execute
* it via the general ExecEvalExpr() code. That code path will not
* support set-returning functions buried in the expression, though.
*/
if (IsA(expr, FuncExpr))
{
FuncExpr *func = (FuncExpr *) expr;
state->funcReturnsSet = func->funcretset;
state->args = ExecInitExprList(func->args, parent);
init_sexpr(func->funcid, func->inputcollid, state,
econtext->ecxt_per_query_memory, func->funcretset, false);
}
else
{
state->elidedFuncState = ExecInitExpr(expr, parent);
}
return state;
}
/*
* ExecMakeTableFunctionResult
*
* Evaluate a table function, producing a materialized result in a Tuplestore
* object.
*
* This is used by nodeFunctionscan.c.
*/
Tuplestorestate *
ExecMakeTableFunctionResult(SetExprState *setexpr,
ExprContext *econtext,
MemoryContext argContext,
TupleDesc expectedDesc,
bool randomAccess)
{
Tuplestorestate *tupstore = NULL;
TupleDesc tupdesc = NULL;
Oid funcrettype;
bool returnsTuple;
bool returnsSet = false;
FunctionCallInfoData fcinfo;
PgStat_FunctionCallUsage fcusage;
ReturnSetInfo rsinfo;
HeapTupleData tmptup;
MemoryContext callerContext;
MemoryContext oldcontext;
bool first_time = true;
callerContext = CurrentMemoryContext;
funcrettype = exprType((Node *) setexpr->expr);
returnsTuple = type_is_rowtype(funcrettype);
/*
* Prepare a resultinfo node for communication. We always do this even if
* not expecting a set result, so that we can pass expectedDesc. In the
* generic-expression case, the expression doesn't actually get to see the
* resultinfo, but set it up anyway because we use some of the fields as
* our own state variables.
*/
rsinfo.type = T_ReturnSetInfo;
rsinfo.econtext = econtext;
rsinfo.expectedDesc = expectedDesc;
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize | SFRM_Materialize_Preferred);
if (randomAccess)
rsinfo.allowedModes |= (int) SFRM_Materialize_Random;
rsinfo.returnMode = SFRM_ValuePerCall;
/* isDone is filled below */
rsinfo.setResult = NULL;
rsinfo.setDesc = NULL;
/*
* Normally the passed expression tree will be a SetExprState, since the
* grammar only allows a function call at the top level of a table
* function reference. However, if the function doesn't return set then
* the planner might have replaced the function call via constant-folding
* or inlining. So if we see any other kind of expression node, execute
* it via the general ExecEvalExpr() code; the only difference is that we
* don't get a chance to pass a special ReturnSetInfo to any functions
* buried in the expression.
*/
if (!setexpr->elidedFuncState)
{
/*
* This path is similar to ExecMakeFunctionResultSet.
*/
returnsSet = setexpr->funcReturnsSet;
InitFunctionCallInfoData(fcinfo, &(setexpr->func),
list_length(setexpr->args),
setexpr->fcinfo_data.fncollation,
NULL, (Node *) &rsinfo);
/*
* Evaluate the function's argument list.
*
* We can't do this in the per-tuple context: the argument values
* would disappear when we reset that context in the inner loop. And
* the caller's CurrentMemoryContext is typically a query-lifespan
* context, so we don't want to leak memory there. We require the
* caller to pass a separate memory context that can be used for this,
* and can be reset each time through to avoid bloat.
*/
MemoryContextReset(argContext);
oldcontext = MemoryContextSwitchTo(argContext);
ExecEvalFuncArgs(&fcinfo, setexpr->args, econtext);
MemoryContextSwitchTo(oldcontext);
/*
* If function is strict, and there are any NULL arguments, skip
* calling the function and act like it returned NULL (or an empty
* set, in the returns-set case).
*/
if (setexpr->func.fn_strict)
{
int i;
for (i = 0; i < fcinfo.nargs; i++)
{
if (fcinfo.argnull[i])
goto no_function_result;
}
}
}
else
{
/* Treat setexpr as a generic expression */
InitFunctionCallInfoData(fcinfo, NULL, 0, InvalidOid, NULL, NULL);
}
/*
* Switch to short-lived context for calling the function or expression.
*/
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
/*
* Loop to handle the ValuePerCall protocol (which is also the same
* behavior needed in the generic ExecEvalExpr path).
*/
for (;;)
{
Datum result;
CHECK_FOR_INTERRUPTS();
/*
* reset per-tuple memory context before each call of the function or
* expression. This cleans up any local memory the function may leak
* when called.
*/
ResetExprContext(econtext);
/* Call the function or expression one time */
if (!setexpr->elidedFuncState)
{
pgstat_init_function_usage(&fcinfo, &fcusage);
fcinfo.isnull = false;
rsinfo.isDone = ExprSingleResult;
result = FunctionCallInvoke(&fcinfo);
pgstat_end_function_usage(&fcusage,
rsinfo.isDone != ExprMultipleResult);
}
else
{
result =
ExecEvalExpr(setexpr->elidedFuncState, econtext, &fcinfo.isnull);
rsinfo.isDone = ExprSingleResult;
}
/* Which protocol does function want to use? */
if (rsinfo.returnMode == SFRM_ValuePerCall)
{
/*
* Check for end of result set.
*/
if (rsinfo.isDone == ExprEndResult)
break;
/*
* If first time through, build tuplestore for result. For a
* scalar function result type, also make a suitable tupdesc.
*/
if (first_time)
{
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
rsinfo.setResult = tupstore;
if (!returnsTuple)
{
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
"column",
funcrettype,
-1,
0);
rsinfo.setDesc = tupdesc;
}
MemoryContextSwitchTo(oldcontext);
}
/*
* Store current resultset item.
*/
if (returnsTuple)
{
if (!fcinfo.isnull)
{
HeapTupleHeader td = DatumGetHeapTupleHeader(result);
if (tupdesc == NULL)
{
/*
* This is the first non-NULL result from the
* function. Use the type info embedded in the
* rowtype Datum to look up the needed tupdesc. Make
* a copy for the query.
*/
oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tupdesc = lookup_rowtype_tupdesc_copy(HeapTupleHeaderGetTypeId(td),
HeapTupleHeaderGetTypMod(td));
rsinfo.setDesc = tupdesc;
MemoryContextSwitchTo(oldcontext);
}
else
{
/*
* Verify all later returned rows have same subtype;
* necessary in case the type is RECORD.
*/
if (HeapTupleHeaderGetTypeId(td) != tupdesc->tdtypeid ||
HeapTupleHeaderGetTypMod(td) != tupdesc->tdtypmod)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("rows returned by function are not all of the same row type")));
}
/*
* tuplestore_puttuple needs a HeapTuple not a bare
* HeapTupleHeader, but it doesn't need all the fields.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
tmptup.t_data = td;
tuplestore_puttuple(tupstore, &tmptup);
}
else
{
/*
* NULL result from a tuple-returning function; expand it
* to a row of all nulls. We rely on the expectedDesc to
* form such rows. (Note: this would be problematic if
* tuplestore_putvalues saved the tdtypeid/tdtypmod from
* the provided descriptor, since that might not match
* what we get from the function itself. But it doesn't.)
*/
int natts = expectedDesc->natts;
bool *nullflags;
nullflags = (bool *) palloc(natts * sizeof(bool));
memset(nullflags, true, natts * sizeof(bool));
tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
}
}
else
{
/* Scalar-type case: just store the function result */
tuplestore_putvalues(tupstore, tupdesc, &result, &fcinfo.isnull);
}
/*
* Are we done?
*/
if (rsinfo.isDone != ExprMultipleResult)
break;
}
else if (rsinfo.returnMode == SFRM_Materialize)
{
/* check we're on the same page as the function author */
if (!first_time || rsinfo.isDone != ExprSingleResult)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
errmsg("table-function protocol for materialize mode was not followed")));
/* Done evaluating the set result */
break;
}
else
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
errmsg("unrecognized table-function returnMode: %d",
(int) rsinfo.returnMode)));
first_time = false;
}
no_function_result:
/*
* If we got nothing from the function (ie, an empty-set or NULL result),
* we have to create the tuplestore to return, and if it's a
* non-set-returning function then insert a single all-nulls row. As
* above, we depend on the expectedDesc to manufacture the dummy row.
*/
if (rsinfo.setResult == NULL)
{
MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
tupstore = tuplestore_begin_heap(randomAccess, false, work_mem);
rsinfo.setResult = tupstore;
if (!returnsSet)
{
int natts = expectedDesc->natts;
bool *nullflags;
MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
nullflags = (bool *) palloc(natts * sizeof(bool));
memset(nullflags, true, natts * sizeof(bool));
tuplestore_putvalues(tupstore, expectedDesc, NULL, nullflags);
}
}
/*
* If function provided a tupdesc, cross-check it. We only really need to
* do this for functions returning RECORD, but might as well do it always.
*/
if (rsinfo.setDesc)
{
tupledesc_match(expectedDesc, rsinfo.setDesc);
/*
* If it is a dynamically-allocated TupleDesc, free it: it is
* typically allocated in a per-query context, so we must avoid
* leaking it across multiple usages.
*/
if (rsinfo.setDesc->tdrefcount == -1)
FreeTupleDesc(rsinfo.setDesc);
}
MemoryContextSwitchTo(callerContext);
/* All done, pass back the tuplestore */
return rsinfo.setResult;
}
/*
* Prepare targetlist SRF function call for execution.
*
* This is used by nodeProjectSet.c.
*/
SetExprState *
ExecInitFunctionResultSet(Expr *expr,
ExprContext *econtext, PlanState *parent)
{
SetExprState *state = makeNode(SetExprState);
state->funcReturnsSet = true;
state->expr = expr;
state->func.fn_oid = InvalidOid;
/*
* Initialize metadata. The expression node could be either a FuncExpr or
* an OpExpr.
*/
if (IsA(expr, FuncExpr))
{
FuncExpr *func = (FuncExpr *) expr;
state->args = ExecInitExprList(func->args, parent);
init_sexpr(func->funcid, func->inputcollid, state,
econtext->ecxt_per_query_memory, true, true);
}
else if (IsA(expr, OpExpr))
{
OpExpr *op = (OpExpr *) expr;
state->args = ExecInitExprList(op->args, parent);
init_sexpr(op->opfuncid, op->inputcollid, state,
econtext->ecxt_per_query_memory, true, true);
}
else
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(expr));
/* shouldn't get here unless the selected function returns set */
Assert(state->func.fn_retset);
return state;
}
/*
* ExecMakeFunctionResultSet
*
* Evaluate the arguments to a set-returning function and then call the
* function itself. The argument expressions may not contain set-returning
* functions (the planner is supposed to have separated evaluation for those).
*
* This is used by nodeProjectSet.c.
*/
Datum
ExecMakeFunctionResultSet(SetExprState *fcache,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone)
{
List *arguments;
Datum result;
FunctionCallInfo fcinfo;
PgStat_FunctionCallUsage fcusage;
ReturnSetInfo rsinfo;
bool callit;
int i;
restart:
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
/*
* If a previous call of the function returned a set result in the form of
* a tuplestore, continue reading rows from the tuplestore until it's
* empty.
*/
if (fcache->funcResultStore)
{
if (tuplestore_gettupleslot(fcache->funcResultStore, true, false,
fcache->funcResultSlot))
{
*isDone = ExprMultipleResult;
if (fcache->funcReturnsTuple)
{
/* We must return the whole tuple as a Datum. */
*isNull = false;
return ExecFetchSlotTupleDatum(fcache->funcResultSlot);
}
else
{
/* Extract the first column and return it as a scalar. */
return slot_getattr(fcache->funcResultSlot, 1, isNull);
}
}
/* Exhausted the tuplestore, so clean up */
tuplestore_end(fcache->funcResultStore);
fcache->funcResultStore = NULL;
*isDone = ExprEndResult;
*isNull = true;
return (Datum) 0;
}
/*
* arguments is a list of expressions to evaluate before passing to the
* function manager. We skip the evaluation if it was already done in the
* previous call (ie, we are continuing the evaluation of a set-valued
* function). Otherwise, collect the current argument values into fcinfo.
*/
fcinfo = &fcache->fcinfo_data;
arguments = fcache->args;
if (!fcache->setArgsValid)
ExecEvalFuncArgs(fcinfo, arguments, econtext);
else
{
/* Reset flag (we may set it again below) */
fcache->setArgsValid = false;
}
/*
* Now call the function, passing the evaluated parameter values.
*/
/* Prepare a resultinfo node for communication. */
fcinfo->resultinfo = (Node *) &rsinfo;
rsinfo.type = T_ReturnSetInfo;
rsinfo.econtext = econtext;
rsinfo.expectedDesc = fcache->funcResultDesc;
rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize);
/* note we do not set SFRM_Materialize_Random or _Preferred */
rsinfo.returnMode = SFRM_ValuePerCall;
/* isDone is filled below */
rsinfo.setResult = NULL;
rsinfo.setDesc = NULL;
/*
* If function is strict, and there are any NULL arguments, skip calling
* the function.
*/
callit = true;
if (fcache->func.fn_strict)
{
for (i = 0; i < fcinfo->nargs; i++)
{
if (fcinfo->argnull[i])
{
callit = false;
break;
}
}
}
if (callit)
{
pgstat_init_function_usage(fcinfo, &fcusage);
fcinfo->isnull = false;
rsinfo.isDone = ExprSingleResult;
result = FunctionCallInvoke(fcinfo);
*isNull = fcinfo->isnull;
*isDone = rsinfo.isDone;
pgstat_end_function_usage(&fcusage,
rsinfo.isDone != ExprMultipleResult);
}
else
{
/* for a strict SRF, result for NULL is an empty set */
result = (Datum) 0;
*isNull = true;
*isDone = ExprEndResult;
}
/* Which protocol does function want to use? */
if (rsinfo.returnMode == SFRM_ValuePerCall)
{
if (*isDone != ExprEndResult)
{
/*
* Save the current argument values to re-use on the next call.
*/
if (*isDone == ExprMultipleResult)
{
fcache->setArgsValid = true;
/* Register cleanup callback if we didn't already */
if (!fcache->shutdown_reg)
{
RegisterExprContextCallback(econtext,
ShutdownSetExpr,
PointerGetDatum(fcache));
fcache->shutdown_reg = true;
}
}
}
}
else if (rsinfo.returnMode == SFRM_Materialize)
{
/* check we're on the same page as the function author */
if (rsinfo.isDone != ExprSingleResult)
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
errmsg("table-function protocol for materialize mode was not followed")));
if (rsinfo.setResult != NULL)
{
/* prepare to return values from the tuplestore */
ExecPrepareTuplestoreResult(fcache, econtext,
rsinfo.setResult,
rsinfo.setDesc);
/* loop back to top to start returning from tuplestore */
goto restart;
}
/* if setResult was left null, treat it as empty set */
*isDone = ExprEndResult;
*isNull = true;
result = (Datum) 0;
}
else
ereport(ERROR,
(errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED),
errmsg("unrecognized table-function returnMode: %d",
(int) rsinfo.returnMode)));
return result;
}
/*
* init_sexpr - initialize a SetExprState node during first use
*/
static void
init_sexpr(Oid foid, Oid input_collation, SetExprState *sexpr,
MemoryContext sexprCxt, bool allowSRF, bool needDescForSRF)
{
AclResult aclresult;
/* Check permission to call function */
aclresult = pg_proc_aclcheck(foid, GetUserId(), ACL_EXECUTE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(foid));
InvokeFunctionExecuteHook(foid);
/*
* Safety check on nargs. Under normal circumstances this should never
* fail, as parser should check sooner. But possibly it might fail if
* server has been compiled with FUNC_MAX_ARGS smaller than some functions
* declared in pg_proc?
*/
if (list_length(sexpr->args) > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("cannot pass more than %d argument to a function",
"cannot pass more than %d arguments to a function",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
/* Set up the primary fmgr lookup information */
fmgr_info_cxt(foid, &(sexpr->func), sexprCxt);
fmgr_info_set_expr((Node *) sexpr->expr, &(sexpr->func));
/* Initialize the function call parameter struct as well */
InitFunctionCallInfoData(sexpr->fcinfo_data, &(sexpr->func),
list_length(sexpr->args),
input_collation, NULL, NULL);
/* If function returns set, check if that's allowed by caller */
if (sexpr->func.fn_retset && !allowSRF)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("set-valued function called in context that cannot accept a set")));
/* Otherwise, caller should have marked the sexpr correctly */
Assert(sexpr->func.fn_retset == sexpr->funcReturnsSet);
/* If function returns set, prepare expected tuple descriptor */
if (sexpr->func.fn_retset && needDescForSRF)
{
TypeFuncClass functypclass;
Oid funcrettype;
TupleDesc tupdesc;
MemoryContext oldcontext;
functypclass = get_expr_result_type(sexpr->func.fn_expr,
&funcrettype,
&tupdesc);
/* Must save tupdesc in sexpr's context */
oldcontext = MemoryContextSwitchTo(sexprCxt);
if (functypclass == TYPEFUNC_COMPOSITE)
{
/* Composite data type, e.g. a table's row type */
Assert(tupdesc);
/* Must copy it out of typcache for safety */
sexpr->funcResultDesc = CreateTupleDescCopy(tupdesc);
sexpr->funcReturnsTuple = true;
}
else if (functypclass == TYPEFUNC_SCALAR)
{
/* Base data type, i.e. scalar */
tupdesc = CreateTemplateTupleDesc(1, false);
TupleDescInitEntry(tupdesc,
(AttrNumber) 1,
NULL,
funcrettype,
-1,
0);
sexpr->funcResultDesc = tupdesc;
sexpr->funcReturnsTuple = false;
}
else if (functypclass == TYPEFUNC_RECORD)
{
/* This will work if function doesn't need an expectedDesc */
sexpr->funcResultDesc = NULL;
sexpr->funcReturnsTuple = true;
}
else
{
/* Else, we will fail if function needs an expectedDesc */
sexpr->funcResultDesc = NULL;
}
MemoryContextSwitchTo(oldcontext);
}
else
sexpr->funcResultDesc = NULL;
/* Initialize additional state */
sexpr->funcResultStore = NULL;
sexpr->funcResultSlot = NULL;
sexpr->shutdown_reg = false;
}
/*
* callback function in case a SetExprState needs to be shut down before it
* has been run to completion
*/
static void
ShutdownSetExpr(Datum arg)
{
SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg));
/* If we have a slot, make sure it's let go of any tuplestore pointer */
if (sexpr->funcResultSlot)
ExecClearTuple(sexpr->funcResultSlot);
/* Release any open tuplestore */
if (sexpr->funcResultStore)
tuplestore_end(sexpr->funcResultStore);
sexpr->funcResultStore = NULL;
/* Clear any active set-argument state */
sexpr->setArgsValid = false;
/* execUtils will deregister the callback... */
sexpr->shutdown_reg = false;
}
/*
* Evaluate arguments for a function.
*/
static void
ExecEvalFuncArgs(FunctionCallInfo fcinfo,
List *argList,
ExprContext *econtext)
{
int i;
ListCell *arg;
i = 0;
foreach(arg, argList)
{
ExprState *argstate = (ExprState *) lfirst(arg);
fcinfo->arg[i] = ExecEvalExpr(argstate,
econtext,
&fcinfo->argnull[i]);
i++;
}
Assert(i == fcinfo->nargs);
}
/*
* ExecPrepareTuplestoreResult
*
* Subroutine for ExecMakeFunctionResultSet: prepare to extract rows from a
* tuplestore function result. We must set up a funcResultSlot (unless
* already done in a previous call cycle) and verify that the function
* returned the expected tuple descriptor.
*/
static void
ExecPrepareTuplestoreResult(SetExprState *sexpr,
ExprContext *econtext,
Tuplestorestate *resultStore,
TupleDesc resultDesc)
{
sexpr->funcResultStore = resultStore;
if (sexpr->funcResultSlot == NULL)
{
/* Create a slot so we can read data out of the tuplestore */
TupleDesc slotDesc;
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(sexpr->func.fn_mcxt);
/*
* If we were not able to determine the result rowtype from context,
* and the function didn't return a tupdesc, we have to fail.
*/
if (sexpr->funcResultDesc)
slotDesc = sexpr->funcResultDesc;
else if (resultDesc)
{
/* don't assume resultDesc is long-lived */
slotDesc = CreateTupleDescCopy(resultDesc);
}
else
{
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("function returning setof record called in "
"context that cannot accept type record")));
slotDesc = NULL; /* keep compiler quiet */
}
sexpr->funcResultSlot = MakeSingleTupleTableSlot(slotDesc);
MemoryContextSwitchTo(oldcontext);
}
/*
* If function provided a tupdesc, cross-check it. We only really need to
* do this for functions returning RECORD, but might as well do it always.
*/
if (resultDesc)
{
if (sexpr->funcResultDesc)
tupledesc_match(sexpr->funcResultDesc, resultDesc);
/*
* If it is a dynamically-allocated TupleDesc, free it: it is
* typically allocated in a per-query context, so we must avoid
* leaking it across multiple usages.
*/
if (resultDesc->tdrefcount == -1)
FreeTupleDesc(resultDesc);
}
/* Register cleanup callback if we didn't already */
if (!sexpr->shutdown_reg)
{
RegisterExprContextCallback(econtext,
ShutdownSetExpr,
PointerGetDatum(sexpr));
sexpr->shutdown_reg = true;
}
}
/*
* Check that function result tuple type (src_tupdesc) matches or can
* be considered to match what the query expects (dst_tupdesc). If
* they don't match, ereport.
*
* We really only care about number of attributes and data type.
* Also, we can ignore type mismatch on columns that are dropped in the
* destination type, so long as the physical storage matches. This is
* helpful in some cases involving out-of-date cached plans.
*/
static void
tupledesc_match(TupleDesc dst_tupdesc, TupleDesc src_tupdesc)
{
int i;
if (dst_tupdesc->natts != src_tupdesc->natts)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function return row and query-specified return row do not match"),
errdetail_plural("Returned row contains %d attribute, but query expects %d.",
"Returned row contains %d attributes, but query expects %d.",
src_tupdesc->natts,
src_tupdesc->natts, dst_tupdesc->natts)));
for (i = 0; i < dst_tupdesc->natts; i++)
{
Form_pg_attribute dattr = dst_tupdesc->attrs[i];
Form_pg_attribute sattr = src_tupdesc->attrs[i];
if (IsBinaryCoercible(sattr->atttypid, dattr->atttypid))
continue; /* no worries */
if (!dattr->attisdropped)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function return row and query-specified return row do not match"),
errdetail("Returned type %s at ordinal position %d, but query expects %s.",
format_type_be(sattr->atttypid),
i + 1,
format_type_be(dattr->atttypid))));
if (dattr->attlen != sattr->attlen ||
dattr->attalign != sattr->attalign)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("function return row and query-specified return row do not match"),
errdetail("Physical storage mismatch on dropped attribute at ordinal position %d.",
i + 1)));
}
}

View File

@ -123,7 +123,7 @@ ExecScan(ScanState *node,
ExecScanRecheckMtd recheckMtd)
{
ExprContext *econtext;
List *qual;
ExprState *qual;
ProjectionInfo *projInfo;
/*
@ -170,7 +170,7 @@ ExecScan(ScanState *node,
if (TupIsNull(slot))
{
if (projInfo)
return ExecClearTuple(projInfo->pi_slot);
return ExecClearTuple(projInfo->pi_state.resultslot);
else
return slot;
}
@ -183,11 +183,11 @@ ExecScan(ScanState *node,
/*
* check that the current tuple satisfies the qual-clause
*
* check for non-nil qual here to avoid a function call to ExecQual()
* when the qual is nil ... saves only a few cycles, but they add up
* check for non-null qual here to avoid a function call to ExecQual()
* when the qual is null ... saves only a few cycles, but they add up
* ...
*/
if (!qual || ExecQual(qual, econtext, false))
if (qual == NULL || ExecQual(qual, econtext))
{
/*
* Found a satisfactory scan tuple.

View File

@ -31,6 +31,9 @@
* RegisterExprContextCallback Register function shutdown callback
* UnregisterExprContextCallback Deregister function shutdown callback
*
* GetAttributeByName Runtime extraction of columns from tuples.
* GetAttributeByNum
*
* NOTES
* This file has traditionally been the place to stick misc.
* executor support stuff that doesn't really go anyplace else.
@ -44,11 +47,12 @@
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/typcache.h"
static bool get_last_attnums(Node *node, ProjectionInfo *projInfo);
static void ShutdownExprContext(ExprContext *econtext, bool isCommit);
@ -464,186 +468,6 @@ ExecGetResultType(PlanState *planstate)
return slot->tts_tupleDescriptor;
}
/* ----------------
* ExecBuildProjectionInfo
*
* Build a ProjectionInfo node for evaluating the given tlist in the given
* econtext, and storing the result into the tuple slot. (Caller must have
* ensured that tuple slot has a descriptor matching the tlist!) Note that
* the given tlist should be a list of ExprState nodes, not Expr nodes.
*
* inputDesc can be NULL, but if it is not, we check to see whether simple
* Vars in the tlist match the descriptor. It is important to provide
* inputDesc for relation-scan plan nodes, as a cross check that the relation
* hasn't been changed since the plan was made. At higher levels of a plan,
* there is no need to recheck.
* ----------------
*/
ProjectionInfo *
ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot,
TupleDesc inputDesc)
{
ProjectionInfo *projInfo = makeNode(ProjectionInfo);
int len = ExecTargetListLength(targetList);
int *workspace;
int *varSlotOffsets;
int *varNumbers;
int *varOutputCols;
List *exprlist;
int numSimpleVars;
bool directMap;
ListCell *tl;
projInfo->pi_exprContext = econtext;
projInfo->pi_slot = slot;
/* since these are all int arrays, we need do just one palloc */
workspace = (int *) palloc(len * 3 * sizeof(int));
projInfo->pi_varSlotOffsets = varSlotOffsets = workspace;
projInfo->pi_varNumbers = varNumbers = workspace + len;
projInfo->pi_varOutputCols = varOutputCols = workspace + len * 2;
projInfo->pi_lastInnerVar = 0;
projInfo->pi_lastOuterVar = 0;
projInfo->pi_lastScanVar = 0;
/*
* We separate the target list elements into simple Var references and
* expressions which require the full ExecTargetList machinery. To be a
* simple Var, a Var has to be a user attribute and not mismatch the
* inputDesc. (Note: if there is a type mismatch then ExecEvalScalarVar
* will probably throw an error at runtime, but we leave that to it.)
*/
exprlist = NIL;
numSimpleVars = 0;
directMap = true;
foreach(tl, targetList)
{
GenericExprState *gstate = (GenericExprState *) lfirst(tl);
Var *variable = (Var *) gstate->arg->expr;
bool isSimpleVar = false;
if (variable != NULL &&
IsA(variable, Var) &&
variable->varattno > 0)
{
if (!inputDesc)
isSimpleVar = true; /* can't check type, assume OK */
else if (variable->varattno <= inputDesc->natts)
{
Form_pg_attribute attr;
attr = inputDesc->attrs[variable->varattno - 1];
if (!attr->attisdropped && variable->vartype == attr->atttypid)
isSimpleVar = true;
}
}
if (isSimpleVar)
{
TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr;
AttrNumber attnum = variable->varattno;
varNumbers[numSimpleVars] = attnum;
varOutputCols[numSimpleVars] = tle->resno;
if (tle->resno != numSimpleVars + 1)
directMap = false;
switch (variable->varno)
{
case INNER_VAR:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_innertuple);
if (projInfo->pi_lastInnerVar < attnum)
projInfo->pi_lastInnerVar = attnum;
break;
case OUTER_VAR:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_outertuple);
if (projInfo->pi_lastOuterVar < attnum)
projInfo->pi_lastOuterVar = attnum;
break;
/* INDEX_VAR is handled by default case */
default:
varSlotOffsets[numSimpleVars] = offsetof(ExprContext,
ecxt_scantuple);
if (projInfo->pi_lastScanVar < attnum)
projInfo->pi_lastScanVar = attnum;
break;
}
numSimpleVars++;
}
else
{
/* Not a simple variable, add it to generic targetlist */
exprlist = lappend(exprlist, gstate);
/* Examine expr to include contained Vars in lastXXXVar counts */
get_last_attnums((Node *) variable, projInfo);
}
}
projInfo->pi_targetlist = exprlist;
projInfo->pi_numSimpleVars = numSimpleVars;
projInfo->pi_directMap = directMap;
return projInfo;
}
/*
* get_last_attnums: expression walker for ExecBuildProjectionInfo
*
* Update the lastXXXVar counts to be at least as large as the largest
* attribute numbers found in the expression
*/
static bool
get_last_attnums(Node *node, ProjectionInfo *projInfo)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
Var *variable = (Var *) node;
AttrNumber attnum = variable->varattno;
switch (variable->varno)
{
case INNER_VAR:
if (projInfo->pi_lastInnerVar < attnum)
projInfo->pi_lastInnerVar = attnum;
break;
case OUTER_VAR:
if (projInfo->pi_lastOuterVar < attnum)
projInfo->pi_lastOuterVar = attnum;
break;
/* INDEX_VAR is handled by default case */
default:
if (projInfo->pi_lastScanVar < attnum)
projInfo->pi_lastScanVar = 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
* overall targetlist'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,
(void *) projInfo);
}
/* ----------------
* ExecAssignProjectionInfo
@ -659,9 +483,10 @@ ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc)
{
planstate->ps_ProjInfo =
ExecBuildProjectionInfo(planstate->targetlist,
ExecBuildProjectionInfo(planstate->plan->targetlist,
planstate->ps_ExprContext,
planstate->ps_ResultTupleSlot,
planstate,
inputDesc);
}
@ -1009,3 +834,152 @@ ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate)
}
}
}
/*
* GetAttributeByName
* GetAttributeByNum
*
* These functions return the value of the requested attribute
* out of the given tuple Datum.
* C functions which take a tuple as an argument are expected
* to use these. Ex: overpaid(EMP) might call GetAttributeByNum().
* Note: these are actually rather slow because they do a typcache
* lookup on each call.
*/
Datum
GetAttributeByName(HeapTupleHeader tuple, const char *attname, bool *isNull)
{
AttrNumber attrno;
Datum result;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
int i;
if (attname == NULL)
elog(ERROR, "invalid attribute name");
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
if (tuple == NULL)
{
/* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
attrno = InvalidAttrNumber;
for (i = 0; i < tupDesc->natts; i++)
{
if (namestrcmp(&(tupDesc->attrs[i]->attname), attname) == 0)
{
attrno = tupDesc->attrs[i]->attnum;
break;
}
}
if (attrno == InvalidAttrNumber)
elog(ERROR, "attribute \"%s\" does not exist", attname);
/*
* heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all
* the fields in the struct just in case user tries to inspect system
* columns.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuple;
result = heap_getattr(&tmptup,
attrno,
tupDesc,
isNull);
ReleaseTupleDesc(tupDesc);
return result;
}
Datum
GetAttributeByNum(HeapTupleHeader tuple,
AttrNumber attrno,
bool *isNull)
{
Datum result;
Oid tupType;
int32 tupTypmod;
TupleDesc tupDesc;
HeapTupleData tmptup;
if (!AttributeNumberIsValid(attrno))
elog(ERROR, "invalid attribute number %d", attrno);
if (isNull == NULL)
elog(ERROR, "a NULL isNull pointer was passed");
if (tuple == NULL)
{
/* Kinda bogus but compatible with old behavior... */
*isNull = true;
return (Datum) 0;
}
tupType = HeapTupleHeaderGetTypeId(tuple);
tupTypmod = HeapTupleHeaderGetTypMod(tuple);
tupDesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
/*
* heap_getattr needs a HeapTuple not a bare HeapTupleHeader. We set all
* the fields in the struct just in case user tries to inspect system
* columns.
*/
tmptup.t_len = HeapTupleHeaderGetDatumLength(tuple);
ItemPointerSetInvalid(&(tmptup.t_self));
tmptup.t_tableOid = InvalidOid;
tmptup.t_data = tuple;
result = heap_getattr(&tmptup,
attrno,
tupDesc,
isNull);
ReleaseTupleDesc(tupDesc);
return result;
}
/*
* Number of items in a tlist (including any resjunk items!)
*/
int
ExecTargetListLength(List *targetlist)
{
/* This used to be more complex, but fjoins are dead */
return list_length(targetlist);
}
/*
* Number of items in a tlist, not including any resjunk items
*/
int
ExecCleanTargetListLength(List *targetlist)
{
int len = 0;
ListCell *tl;
foreach(tl, targetlist)
{
TargetEntry *curTle = castNode(TargetEntry, lfirst(tl));
if (!curTle->resjunk)
len++;
}
return len;
}

View File

@ -1279,7 +1279,7 @@ fmgr_sql(PG_FUNCTION_ARGS)
rsi->returnMode = SFRM_Materialize;
rsi->setResult = fcache->tstore;
fcache->tstore = NULL;
/* must copy desc because execQual will free it */
/* must copy desc because execSRF.c will free it */
if (fcache->junkFilter)
rsi->setDesc = CreateTupleDescCopy(fcache->junkFilter->jf_cleanTupType);

View File

@ -1639,7 +1639,7 @@ project_aggregates(AggState *aggstate)
/*
* Check the qual (HAVING clause); if the group does not match, ignore it.
*/
if (ExecQual(aggstate->ss.ps.qual, econtext, false))
if (ExecQual(aggstate->ss.ps.qual, econtext))
{
/*
* Form and return projection tuple using the aggregate results and
@ -2501,18 +2501,17 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/*
* initialize child expressions
*
* Note: ExecInitExpr finds Aggrefs for us, and also checks that no aggs
* contain other agg calls in their arguments. This would make no sense
* under SQL semantics anyway (and it's forbidden by the spec). Because
* that is true, we don't need to worry about evaluating the aggs in any
* particular order.
* We rely on the parser to have checked that no aggs contain other agg
* calls in their arguments. This would make no sense under SQL semantics
* (and it's forbidden by the spec). Because it is true, we don't need to
* worry about evaluating the aggs in any particular order.
*
* Note: execExpr.c finds Aggrefs for us, and adds their AggrefExprState
* nodes to aggstate->aggs. Aggrefs in the qual are found here; Aggrefs
* in the targetlist are found during ExecAssignProjectionInfo, below.
*/
aggstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) aggstate);
aggstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) aggstate);
aggstate->ss.ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) aggstate);
/*
* Initialize child nodes.
@ -2540,7 +2539,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
ExecAssignProjectionInfo(&aggstate->ss.ps, NULL);
/*
* get the count of aggregates in targetlist and quals
* We should now have found all Aggrefs in the targetlist and quals.
*/
numaggs = aggstate->numaggs;
Assert(numaggs == list_length(aggstate->aggs));
@ -2724,7 +2723,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
foreach(l, aggstate->aggs)
{
AggrefExprState *aggrefstate = (AggrefExprState *) lfirst(l);
Aggref *aggref = (Aggref *) aggrefstate->xprstate.expr;
Aggref *aggref = aggrefstate->aggref;
AggStatePerAgg peragg;
AggStatePerTrans pertrans;
int existing_aggno;
@ -3024,11 +3023,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
/* and then create a projection for that targetlist */
aggstate->evaldesc = ExecTypeFromTL(combined_inputeval, false);
aggstate->evalslot = ExecInitExtraTupleSlot(estate);
combined_inputeval = (List *) ExecInitExpr((Expr *) combined_inputeval,
(PlanState *) aggstate);
aggstate->evalproj = ExecBuildProjectionInfo(combined_inputeval,
aggstate->tmpcontext,
aggstate->evalslot,
&aggstate->ss.ps,
NULL);
ExecSetSlotDescriptor(aggstate->evalslot, aggstate->evaldesc);
@ -3206,8 +3204,8 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans,
naggs = aggstate->numaggs;
pertrans->aggfilter = ExecInitExpr(aggref->aggfilter,
(PlanState *) aggstate);
pertrans->aggdirectargs = (List *) ExecInitExpr((Expr *) aggref->aggdirectargs,
(PlanState *) aggstate);
pertrans->aggdirectargs = ExecInitExprList(aggref->aggdirectargs,
(PlanState *) aggstate);
/*
* Complain if the aggregate's arguments contain any aggregates; nested

View File

@ -319,7 +319,7 @@ BitmapHeapNext(BitmapHeapScanState *node)
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->bitmapqualorig, econtext, false))
if (!ExecQual(node->bitmapqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
@ -654,7 +654,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot)
ResetExprContext(econtext);
return ExecQual(node->bitmapqualorig, econtext, false);
return ExecQual(node->bitmapqualorig, econtext);
}
/* ----------------------------------------------------------------
@ -837,15 +837,10 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->bitmapqualorig = (List *)
ExecInitExpr((Expr *) node->bitmapqualorig,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
scanstate->bitmapqualorig =
ExecInitQual(node->bitmapqualorig, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -242,12 +242,8 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -49,12 +49,8 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags)
ExecAssignExprContext(estate, &css->ss.ps);
/* initialize child expressions */
css->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) cscan->scan.plan.targetlist,
(PlanState *) css);
css->ss.ps.qual = (List *)
ExecInitExpr((Expr *) cscan->scan.plan.qual,
(PlanState *) css);
css->ss.ps.qual =
ExecInitQual(cscan->scan.plan.qual, (PlanState *) css);
/* tuple table initialization */
ExecInitScanTupleSlot(estate, &css->ss);

View File

@ -101,7 +101,7 @@ ForeignRecheck(ForeignScanState *node, TupleTableSlot *slot)
!fdwroutine->RecheckForeignScan(node, slot))
return false;
return ExecQual(node->fdw_recheck_quals, econtext, false);
return ExecQual(node->fdw_recheck_quals, econtext);
}
/* ----------------------------------------------------------------
@ -155,15 +155,10 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->fdw_recheck_quals = (List *)
ExecInitExpr((Expr *) node->fdw_recheck_quals,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
scanstate->fdw_recheck_quals =
ExecInitQual(node->fdw_recheck_quals, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -35,7 +35,7 @@
*/
typedef struct FunctionScanPerFuncState
{
ExprState *funcexpr; /* state of the expression being evaluated */
SetExprState *setexpr; /* state of the expression being evaluated */
TupleDesc tupdesc; /* desc of the function result type */
int colcount; /* expected number of result columns */
Tuplestorestate *tstore; /* holds the function result set */
@ -92,7 +92,7 @@ FunctionNext(FunctionScanState *node)
if (tstore == NULL)
{
node->funcstates[0].tstore = tstore =
ExecMakeTableFunctionResult(node->funcstates[0].funcexpr,
ExecMakeTableFunctionResult(node->funcstates[0].setexpr,
node->ss.ps.ps_ExprContext,
node->argcontext,
node->funcstates[0].tupdesc,
@ -151,7 +151,7 @@ FunctionNext(FunctionScanState *node)
if (fs->tstore == NULL)
{
fs->tstore =
ExecMakeTableFunctionResult(fs->funcexpr,
ExecMakeTableFunctionResult(fs->setexpr,
node->ss.ps.ps_ExprContext,
node->argcontext,
fs->tupdesc,
@ -340,12 +340,8 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState));
@ -361,7 +357,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags)
Oid funcrettype;
TupleDesc tupdesc;
fs->funcexpr = ExecInitExpr((Expr *) funcexpr, (PlanState *) scanstate);
fs->setexpr =
ExecInitTableFunctionResult((Expr *) funcexpr,
scanstate->ss.ps.ps_ExprContext,
&scanstate->ss.ps);
/*
* Don't allocate the tuplestores; the actual calls to the functions

View File

@ -81,12 +81,8 @@ ExecInitGather(Gather *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
gatherstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) gatherstate);
gatherstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) gatherstate);
gatherstate->ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) gatherstate);
/*
* tuple table initialization

View File

@ -86,12 +86,8 @@ ExecInitGatherMerge(GatherMerge *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
gm_state->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) gm_state);
gm_state->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) gm_state);
gm_state->ps.qual =
ExecInitQual(node->plan.qual, &gm_state->ps);
/*
* tuple table initialization

View File

@ -85,7 +85,7 @@ ExecGroup(GroupState *node)
* Check the qual (HAVING clause); if the group does not match, ignore
* it and fall into scan loop.
*/
if (ExecQual(node->ss.ps.qual, econtext, false))
if (ExecQual(node->ss.ps.qual, econtext))
{
/*
* Form and return a projection tuple using the first input tuple.
@ -139,7 +139,7 @@ ExecGroup(GroupState *node)
* Check the qual (HAVING clause); if the group does not match, ignore
* it and loop back to scan the rest of the group.
*/
if (ExecQual(node->ss.ps.qual, econtext, false))
if (ExecQual(node->ss.ps.qual, econtext))
{
/*
* Form and return a projection tuple using the first input tuple.
@ -188,12 +188,8 @@ ExecInitGroup(Group *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
grpstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) grpstate);
grpstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) grpstate);
grpstate->ss.ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) grpstate);
/*
* initialize child nodes

View File

@ -190,12 +190,8 @@ ExecInitHash(Hash *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
hashstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) hashstate);
hashstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) hashstate);
hashstate->ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) hashstate);
/*
* initialize child nodes
@ -1063,7 +1059,7 @@ bool
ExecScanHashBucket(HashJoinState *hjstate,
ExprContext *econtext)
{
List *hjclauses = hjstate->hashclauses;
ExprState *hjclauses = hjstate->hashclauses;
HashJoinTable hashtable = hjstate->hj_HashTable;
HashJoinTuple hashTuple = hjstate->hj_CurTuple;
uint32 hashvalue = hjstate->hj_CurHashValue;
@ -1097,7 +1093,7 @@ ExecScanHashBucket(HashJoinState *hjstate,
/* reset temp memory each time to avoid leaks from qual expr */
ResetExprContext(econtext);
if (ExecQual(hjclauses, econtext, false))
if (ExecQual(hjclauses, econtext))
{
hjstate->hj_CurTuple = hashTuple;
return true;

View File

@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node)
{
PlanState *outerNode;
HashState *hashNode;
List *joinqual;
List *otherqual;
ExprState *joinqual;
ExprState *otherqual;
ExprContext *econtext;
HashJoinTable hashtable;
TupleTableSlot *outerTupleSlot;
@ -275,7 +275,7 @@ ExecHashJoin(HashJoinState *node)
* Only the joinquals determine tuple match status, but all
* quals must pass to actually return the tuple.
*/
if (joinqual == NIL || ExecQual(joinqual, econtext, false))
if (joinqual == NULL || ExecQual(joinqual, econtext))
{
node->hj_MatchedOuter = true;
HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple));
@ -294,8 +294,7 @@ ExecHashJoin(HashJoinState *node)
if (node->js.jointype == JOIN_SEMI)
node->hj_JoinState = HJ_NEED_NEW_OUTER;
if (otherqual == NIL ||
ExecQual(otherqual, econtext, false))
if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
@ -322,8 +321,7 @@ ExecHashJoin(HashJoinState *node)
*/
econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
if (otherqual == NIL ||
ExecQual(otherqual, econtext, false))
if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
@ -350,8 +348,7 @@ ExecHashJoin(HashJoinState *node)
*/
econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot;
if (otherqual == NIL ||
ExecQual(otherqual, econtext, false))
if (otherqual == NULL || ExecQual(otherqual, econtext))
return ExecProject(node->js.ps.ps_ProjInfo);
else
InstrCountFiltered2(node, 1);
@ -411,19 +408,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
hjstate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
(PlanState *) hjstate);
hjstate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
(PlanState *) hjstate);
hjstate->js.ps.qual =
ExecInitQual(node->join.plan.qual, (PlanState *) hjstate);
hjstate->js.jointype = node->join.jointype;
hjstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
(PlanState *) hjstate);
hjstate->hashclauses = (List *)
ExecInitExpr((Expr *) node->hashclauses,
(PlanState *) hjstate);
hjstate->js.joinqual =
ExecInitQual(node->join.joinqual, (PlanState *) hjstate);
hjstate->hashclauses =
ExecInitQual(node->hashclauses, (PlanState *) hjstate);
/*
* initialize child nodes
@ -517,13 +508,14 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
lclauses = NIL;
rclauses = NIL;
hoperators = NIL;
foreach(l, hjstate->hashclauses)
foreach(l, node->hashclauses)
{
FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
OpExpr *hclause = castNode(OpExpr, fstate->xprstate.expr);
OpExpr *hclause = castNode(OpExpr, lfirst(l));
lclauses = lappend(lclauses, linitial(fstate->args));
rclauses = lappend(rclauses, lsecond(fstate->args));
lclauses = lappend(lclauses, ExecInitExpr(linitial(hclause->args),
(PlanState *) hjstate));
rclauses = lappend(rclauses, ExecInitExpr(lsecond(hclause->args),
(PlanState *) hjstate));
hoperators = lappend_oid(hoperators, hclause->opno);
}
hjstate->hj_OuterHashKeys = lclauses;

View File

@ -211,7 +211,7 @@ IndexOnlyNext(IndexOnlyScanState *node)
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->indexqual, econtext, false))
if (!ExecQual(node->indexqual, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
@ -488,15 +488,10 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags)
* Note: we don't initialize all of the indexorderby expression, only the
* sub-parts corresponding to runtime keys (see below).
*/
indexstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) indexstate);
indexstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) indexstate);
indexstate->indexqual = (List *)
ExecInitExpr((Expr *) node->indexqual,
(PlanState *) indexstate);
indexstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
indexstate->indexqual =
ExecInitQual(node->indexqual, (PlanState *) indexstate);
/*
* tuple table initialization

View File

@ -149,7 +149,7 @@ IndexNext(IndexScanState *node)
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->indexqualorig, econtext, false))
if (!ExecQual(node->indexqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
@ -295,7 +295,7 @@ next_indextuple:
{
econtext->ecxt_scantuple = slot;
ResetExprContext(econtext);
if (!ExecQual(node->indexqualorig, econtext, false))
if (!ExecQual(node->indexqualorig, econtext))
{
/* Fails recheck, so drop it and loop back for another */
InstrCountFiltered2(node, 1);
@ -415,7 +415,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot)
ResetExprContext(econtext);
return ExecQual(node->indexqualorig, econtext, false);
return ExecQual(node->indexqualorig, econtext);
}
@ -921,18 +921,12 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags)
* would be nice to improve that. (Problem is that any SubPlans present
* in the expression must be found now...)
*/
indexstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) indexstate);
indexstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) indexstate);
indexstate->indexqualorig = (List *)
ExecInitExpr((Expr *) node->indexqualorig,
(PlanState *) indexstate);
indexstate->indexorderbyorig = (List *)
ExecInitExpr((Expr *) node->indexorderbyorig,
(PlanState *) indexstate);
indexstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) indexstate);
indexstate->indexqualorig =
ExecInitQual(node->indexqualorig, (PlanState *) indexstate);
indexstate->indexorderbyorig =
ExecInitExprList(node->indexorderbyorig, (PlanState *) indexstate);
/*
* tuple table initialization

View File

@ -452,14 +452,14 @@ static TupleTableSlot *
MJFillOuter(MergeJoinState *node)
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
List *otherqual = node->js.ps.qual;
ExprState *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
if (ExecQual(otherqual, econtext))
{
/*
* qualification succeeded. now form the desired projection tuple and
@ -483,14 +483,14 @@ static TupleTableSlot *
MJFillInner(MergeJoinState *node)
{
ExprContext *econtext = node->js.ps.ps_ExprContext;
List *otherqual = node->js.ps.qual;
ExprState *otherqual = node->js.ps.qual;
ResetExprContext(econtext);
econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
if (ExecQual(otherqual, econtext))
{
/*
* qualification succeeded. now form the desired projection tuple and
@ -598,8 +598,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate)
TupleTableSlot *
ExecMergeJoin(MergeJoinState *node)
{
List *joinqual;
List *otherqual;
ExprState *joinqual;
ExprState *otherqual;
bool qualResult;
int compareResult;
PlanState *innerPlan;
@ -785,8 +785,8 @@ ExecMergeJoin(MergeJoinState *node)
innerTupleSlot = node->mj_InnerTupleSlot;
econtext->ecxt_innertuple = innerTupleSlot;
qualResult = (joinqual == NIL ||
ExecQual(joinqual, econtext, false));
qualResult = (joinqual == NULL ||
ExecQual(joinqual, econtext));
MJ_DEBUG_QUAL(joinqual, qualResult);
if (qualResult)
@ -808,8 +808,8 @@ ExecMergeJoin(MergeJoinState *node)
if (node->js.jointype == JOIN_SEMI)
node->mj_JoinState = EXEC_MJ_NEXTOUTER;
qualResult = (otherqual == NIL ||
ExecQual(otherqual, econtext, false));
qualResult = (otherqual == NULL ||
ExecQual(otherqual, econtext));
MJ_DEBUG_QUAL(otherqual, qualResult);
if (qualResult)
@ -1455,16 +1455,11 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
mergestate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
(PlanState *) mergestate);
mergestate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
(PlanState *) mergestate);
mergestate->js.ps.qual =
ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
mergestate->js.jointype = node->join.jointype;
mergestate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
(PlanState *) mergestate);
mergestate->js.joinqual =
ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
mergestate->mj_ConstFalseJoin = false;
/* mergeclauses are handled below */

View File

@ -1152,7 +1152,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
{
ExprContext *econtext = mtstate->ps.ps_ExprContext;
Relation relation = resultRelInfo->ri_RelationDesc;
List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere;
HeapTupleData tuple;
HeapUpdateFailureData hufd;
LockTupleMode lockmode;
@ -1271,7 +1271,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate,
econtext->ecxt_innertuple = excludedSlot;
econtext->ecxt_outertuple = NULL;
if (!ExecQual(onConflictSetWhere, econtext, false))
if (!ExecQual(onConflictSetWhere, econtext))
{
ReleaseBuffer(buffer);
InstrCountFiltered1(&mtstate->ps, 1);
@ -1646,7 +1646,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate = makeNode(ModifyTableState);
mtstate->ps.plan = (Plan *) node;
mtstate->ps.state = estate;
mtstate->ps.targetlist = NIL; /* not actually used */
mtstate->operation = operation;
mtstate->canSetTag = node->canSetTag;
@ -1778,7 +1777,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(ll, wcoList)
{
WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
mtstate->mt_plans[i]);
wcoExprs = lappend(wcoExprs, wcoExpr);
@ -1818,8 +1817,8 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(ll, mapped_wcoList)
{
WithCheckOption *wco = (WithCheckOption *) lfirst(ll);
ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual,
mtstate->mt_plans[i]);
ExprState *wcoExpr = ExecInitQual((List *) wco->qual,
mtstate->mt_plans[i]);
wcoExprs = lappend(wcoExprs, wcoExpr);
}
@ -1852,8 +1851,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
slot = mtstate->ps.ps_ResultTupleSlot;
/* Need an econtext too */
econtext = CreateExprContext(estate);
mtstate->ps.ps_ExprContext = econtext;
if (mtstate->ps.ps_ExprContext == NULL)
ExecAssignExprContext(estate, &mtstate->ps);
econtext = mtstate->ps.ps_ExprContext;
/*
* Build a projection for each result rel.
@ -1862,11 +1862,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
foreach(l, node->returningLists)
{
List *rlist = (List *) lfirst(l);
List *rliststate;
rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rliststate, econtext, slot,
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
@ -1883,16 +1881,14 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
for (i = 0; i < mtstate->mt_num_partitions; i++)
{
Relation partrel = resultRelInfo->ri_RelationDesc;
List *rlist,
*rliststate;
List *rlist;
/* varno = node->nominalRelation */
rlist = map_partition_varattnos(returningList,
node->nominalRelation,
partrel, rel);
rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps);
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rliststate, econtext, slot,
ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
resultRelInfo++;
}
@ -1922,7 +1918,6 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
if (node->onConflictAction == ONCONFLICT_UPDATE)
{
ExprContext *econtext;
ExprState *setexpr;
TupleDesc tupDesc;
/* insert may only have one plan, inheritance is not expanded */
@ -1948,11 +1943,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
mtstate->mt_conflproj = ExecInitExtraTupleSlot(mtstate->ps.state);
ExecSetSlotDescriptor(mtstate->mt_conflproj, tupDesc);
/* build UPDATE SET expression and projection state */
setexpr = ExecInitExpr((Expr *) node->onConflictSet, &mtstate->ps);
/* build UPDATE SET projection state */
resultRelInfo->ri_onConflictSetProj =
ExecBuildProjectionInfo((List *) setexpr, econtext,
mtstate->mt_conflproj,
ExecBuildProjectionInfo(node->onConflictSet, econtext,
mtstate->mt_conflproj, &mtstate->ps,
resultRelInfo->ri_RelationDesc->rd_att);
/* build DO UPDATE WHERE clause expression */
@ -1960,10 +1954,10 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
{
ExprState *qualexpr;
qualexpr = ExecInitExpr((Expr *) node->onConflictWhere,
qualexpr = ExecInitQual((List *) node->onConflictWhere,
&mtstate->ps);
resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr;
resultRelInfo->ri_onConflictSetWhere = qualexpr;
}
}

View File

@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node)
PlanState *outerPlan;
TupleTableSlot *outerTupleSlot;
TupleTableSlot *innerTupleSlot;
List *joinqual;
List *otherqual;
ExprState *joinqual;
ExprState *otherqual;
ExprContext *econtext;
ListCell *lc;
@ -176,7 +176,7 @@ ExecNestLoop(NestLoopState *node)
ENL1_printf("testing qualification for outer-join tuple");
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
if (otherqual == NULL || ExecQual(otherqual, econtext))
{
/*
* qualification was satisfied so we project and return
@ -207,7 +207,7 @@ ExecNestLoop(NestLoopState *node)
*/
ENL1_printf("testing qualification");
if (ExecQual(joinqual, econtext, false))
if (ExecQual(joinqual, econtext))
{
node->nl_MatchedOuter = true;
@ -225,7 +225,7 @@ ExecNestLoop(NestLoopState *node)
if (node->js.jointype == JOIN_SEMI)
node->nl_NeedNewOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
if (otherqual == NULL || ExecQual(otherqual, econtext))
{
/*
* qualification was satisfied so we project and return the
@ -282,16 +282,11 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
nlstate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
(PlanState *) nlstate);
nlstate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
(PlanState *) nlstate);
nlstate->js.ps.qual =
ExecInitQual(node->join.plan.qual, (PlanState *) nlstate);
nlstate->js.jointype = node->join.jointype;
nlstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
(PlanState *) nlstate);
nlstate->js.joinqual =
ExecInitQual(node->join.joinqual, (PlanState *) nlstate);
/*
* initialize child nodes

View File

@ -24,6 +24,7 @@
#include "executor/executor.h"
#include "executor/nodeProjectSet.h"
#include "nodes/nodeFuncs.h"
#include "utils/memutils.h"
@ -119,10 +120,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
{
TupleTableSlot *resultSlot = node->ps.ps_ResultTupleSlot;
ExprContext *econtext = node->ps.ps_ExprContext;
bool hassrf PG_USED_FOR_ASSERTS_ONLY = false;
bool hassrf PG_USED_FOR_ASSERTS_ONLY;
bool hasresult;
int argno;
ListCell *lc;
ExecClearTuple(resultSlot);
@ -132,11 +132,10 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
*/
node->pending_srf_tuples = false;
hasresult = false;
argno = 0;
foreach(lc, node->ps.targetlist)
hassrf = hasresult = false;
for (argno = 0; argno < node->nelems; argno++)
{
GenericExprState *gstate = (GenericExprState *) lfirst(lc);
Node *elem = node->elems[argno];
ExprDoneCond *isdone = &node->elemdone[argno];
Datum *result = &resultSlot->tts_values[argno];
bool *isnull = &resultSlot->tts_isnull[argno];
@ -151,13 +150,12 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
*isnull = true;
hassrf = true;
}
else if (IsA(gstate->arg, FuncExprState) &&
((FuncExprState *) gstate->arg)->funcReturnsSet)
else if (IsA(elem, SetExprState))
{
/*
* Evaluate SRF - possibly continuing previously started output.
*/
*result = ExecMakeFunctionResultSet((FuncExprState *) gstate->arg,
*result = ExecMakeFunctionResultSet((SetExprState *) elem,
econtext, isnull, isdone);
if (*isdone != ExprEndResult)
@ -169,11 +167,9 @@ ExecProjectSRF(ProjectSetState *node, bool continuing)
else
{
/* Non-SRF tlist expression, just evaluate normally. */
*result = ExecEvalExpr(gstate->arg, econtext, isnull);
*result = ExecEvalExpr((ExprState *) elem, econtext, isnull);
*isdone = ExprSingleResult;
}
argno++;
}
/* ProjectSet should not be used if there's no SRFs */
@ -204,6 +200,8 @@ ProjectSetState *
ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
{
ProjectSetState *state;
ListCell *lc;
int off;
/* check for unsupported flags */
Assert(!(eflags & (EXEC_FLAG_MARK | EXEC_FLAG_BACKWARD)));
@ -229,12 +227,7 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
*/
ExecInitResultTupleSlot(estate, &state->ps);
/*
* initialize child expressions
*/
state->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) state);
/* We don't support any qual on ProjectSet nodes */
Assert(node->plan.qual == NIL);
/*
@ -252,11 +245,41 @@ ExecInitProjectSet(ProjectSet *node, EState *estate, int eflags)
*/
ExecAssignResultTypeFromTL(&state->ps);
/* Create workspace for per-SRF is-done state */
/* Create workspace for per-tlist-entry expr state & SRF-is-done state */
state->nelems = list_length(node->plan.targetlist);
state->elems = (Node **)
palloc(sizeof(Node *) * state->nelems);
state->elemdone = (ExprDoneCond *)
palloc(sizeof(ExprDoneCond) * state->nelems);
/*
* Build expressions to evaluate targetlist. We can't use
* ExecBuildProjectionInfo here, since that doesn't deal with SRFs.
* Instead compile each expression separately, using
* ExecInitFunctionResultSet where applicable.
*/
off = 0;
foreach(lc, node->plan.targetlist)
{
TargetEntry *te = (TargetEntry *) lfirst(lc);
Expr *expr = te->expr;
if ((IsA(expr, FuncExpr) &&((FuncExpr *) expr)->funcretset) ||
(IsA(expr, OpExpr) &&((OpExpr *) expr)->opretset))
{
state->elems[off] = (Node *)
ExecInitFunctionResultSet(expr, state->ps.ps_ExprContext,
&state->ps);
}
else
{
Assert(!expression_returns_set((Node *) expr));
state->elems[off] = (Node *) ExecInitExpr(expr, &state->ps);
}
off++;
}
return state;
}

View File

@ -77,9 +77,7 @@ ExecResult(ResultState *node)
*/
if (node->rs_checkqual)
{
bool qualResult = ExecQual((List *) node->resconstantqual,
econtext,
false);
bool qualResult = ExecQual(node->resconstantqual, econtext);
node->rs_checkqual = false;
if (!qualResult)
@ -209,14 +207,10 @@ ExecInitResult(Result *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
resstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) resstate);
resstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) resstate);
resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
(PlanState *) resstate);
resstate->ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) resstate);
resstate->resconstantqual =
ExecInitQual((List *) node->resconstantqual, (PlanState *) resstate);
/*
* initialize child nodes

View File

@ -164,19 +164,12 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
scanstate->args = (List *)
ExecInitExpr((Expr *) tsc->args,
(PlanState *) scanstate);
scanstate->args = ExecInitExprList(tsc->args, (PlanState *) scanstate);
scanstate->repeatable =
ExecInitExpr(tsc->repeatable,
(PlanState *) scanstate);
ExecInitExpr(tsc->repeatable, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -188,12 +188,8 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -39,12 +39,6 @@
#include "utils/memutils.h"
static Datum ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
static Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull);
static Datum ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull);
@ -64,12 +58,12 @@ static bool slotNoNulls(TupleTableSlot *slot);
* This is the main entry point for execution of a regular SubPlan.
* ----------------------------------------------------------------
*/
static Datum
Datum
ExecSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
/* Set non-null as default */
*isNull = false;
@ -95,7 +89,7 @@ ExecHashSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
TupleTableSlot *slot;
@ -217,7 +211,7 @@ ExecScanSubPlan(SubPlanState *node,
ExprContext *econtext,
bool *isNull)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
SubLinkType subLinkType = subplan->subLinkType;
MemoryContext oldcontext;
@ -462,7 +456,7 @@ ExecScanSubPlan(SubPlanState *node,
static void
buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
int ncols = list_length(subplan->paramIds);
ExprContext *innerecontext = node->innerecontext;
@ -596,7 +590,7 @@ buildSubPlanHash(SubPlanState *node, ExprContext *econtext)
* potential for a double free attempt. (XXX possibly no longer needed,
* but can't hurt.)
*/
ExecClearTuple(node->projRight->pi_slot);
ExecClearTuple(node->projRight->pi_state.resultslot);
MemoryContextSwitchTo(oldcontext);
}
@ -694,8 +688,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
SubPlanState *sstate = makeNode(SubPlanState);
EState *estate = parent->state;
sstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecSubPlan;
sstate->xprstate.expr = (Expr *) subplan;
sstate->subplan = subplan;
/* Link the SubPlanState to already-initialized subplan */
sstate->planstate = (PlanState *) list_nth(estate->es_subplanstates,
@ -706,7 +699,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
/* Initialize subexpressions */
sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
sstate->args = ExecInitExprList(subplan->args, parent);
/*
* initialize my state
@ -763,9 +756,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
TupleTableSlot *slot;
List *oplist,
*lefttlist,
*righttlist,
*leftptlist,
*rightptlist;
*righttlist;
ListCell *l;
/* We need a memory context to hold the hash table(s) */
@ -792,35 +783,33 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* use the sub-select's output tuples directly, but that is not the
* case if we had to insert any run-time coercions of the sub-select's
* output datatypes; anyway this avoids storing any resjunk columns
* that might be in the sub-select's output.) Run through the
* that might be in the sub-select's output.) Run through the
* combining expressions to build tlists for the lefthand and
* righthand sides. We need both the ExprState list (for ExecProject)
* and the underlying parse Exprs (for ExecTypeFromTL).
* righthand sides.
*
* We also extract the combining operators themselves to initialize
* the equality and hashing functions for the hash tables.
*/
if (IsA(sstate->testexpr->expr, OpExpr))
if (IsA(subplan->testexpr, OpExpr))
{
/* single combining operator */
oplist = list_make1(sstate->testexpr);
oplist = list_make1(subplan->testexpr);
}
else if (and_clause((Node *) sstate->testexpr->expr))
else if (and_clause((Node *) subplan->testexpr))
{
/* multiple combining operators */
oplist = castNode(BoolExprState, sstate->testexpr)->args;
oplist = castNode(BoolExpr, subplan->testexpr)->args;
}
else
{
/* shouldn't see anything else in a hashable subplan */
elog(ERROR, "unrecognized testexpr type: %d",
(int) nodeTag(sstate->testexpr->expr));
(int) nodeTag(subplan->testexpr));
oplist = NIL; /* keep compiler quiet */
}
Assert(list_length(oplist) == ncols);
lefttlist = righttlist = NIL;
leftptlist = rightptlist = NIL;
sstate->tab_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
sstate->tab_eq_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
sstate->lhs_hash_funcs = (FmgrInfo *) palloc(ncols * sizeof(FmgrInfo));
@ -828,45 +817,30 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
i = 1;
foreach(l, oplist)
{
FuncExprState *fstate = castNode(FuncExprState, lfirst(l));
OpExpr *opexpr = castNode(OpExpr, fstate->xprstate.expr);
ExprState *exstate;
OpExpr *opexpr = castNode(OpExpr, lfirst(l));
Expr *expr;
TargetEntry *tle;
GenericExprState *tlestate;
Oid rhs_eq_oper;
Oid left_hashfn;
Oid right_hashfn;
Assert(list_length(fstate->args) == 2);
Assert(list_length(opexpr->args) == 2);
/* Process lefthand argument */
exstate = (ExprState *) linitial(fstate->args);
expr = exstate->expr;
expr = (Expr *) linitial(opexpr->args);
tle = makeTargetEntry(expr,
i,
NULL,
false);
tlestate = makeNode(GenericExprState);
tlestate->xprstate.expr = (Expr *) tle;
tlestate->xprstate.evalfunc = NULL;
tlestate->arg = exstate;
lefttlist = lappend(lefttlist, tlestate);
leftptlist = lappend(leftptlist, tle);
lefttlist = lappend(lefttlist, tle);
/* Process righthand argument */
exstate = (ExprState *) lsecond(fstate->args);
expr = exstate->expr;
expr = (Expr *) lsecond(opexpr->args);
tle = makeTargetEntry(expr,
i,
NULL,
false);
tlestate = makeNode(GenericExprState);
tlestate->xprstate.expr = (Expr *) tle;
tlestate->xprstate.evalfunc = NULL;
tlestate->arg = exstate;
righttlist = lappend(righttlist, tlestate);
rightptlist = lappend(rightptlist, tle);
righttlist = lappend(righttlist, tle);
/* Lookup the equality function (potentially cross-type) */
fmgr_info(opexpr->opfuncid, &sstate->cur_eq_funcs[i - 1]);
@ -898,20 +872,22 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
* (hack alert!). The righthand expressions will be evaluated in our
* own innerecontext.
*/
tupDesc = ExecTypeFromTL(leftptlist, false);
tupDesc = ExecTypeFromTL(lefttlist, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projLeft = ExecBuildProjectionInfo(lefttlist,
NULL,
slot,
parent,
NULL);
tupDesc = ExecTypeFromTL(rightptlist, false);
tupDesc = ExecTypeFromTL(righttlist, false);
slot = ExecInitExtraTupleSlot(estate);
ExecSetSlotDescriptor(slot, tupDesc);
sstate->projRight = ExecBuildProjectionInfo(righttlist,
sstate->innerecontext,
slot,
sstate->planstate,
NULL);
}
@ -934,7 +910,7 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent)
void
ExecSetParamPlan(SubPlanState *node, ExprContext *econtext)
{
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
PlanState *planstate = node->planstate;
SubLinkType subLinkType = subplan->subLinkType;
MemoryContext oldcontext;
@ -1111,7 +1087,7 @@ void
ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent)
{
PlanState *planstate = node->planstate;
SubPlan *subplan = (SubPlan *) node->xprstate.expr;
SubPlan *subplan = node->subplan;
EState *estate = parent->state;
ListCell *l;
@ -1162,16 +1138,22 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
SubPlan *subplan2;
Cost cost1;
Cost cost2;
ListCell *lc;
asstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecAlternativeSubPlan;
asstate->xprstate.expr = (Expr *) asplan;
asstate->subplan = asplan;
/*
* Initialize subplans. (Can we get away with only initializing the one
* we're going to use?)
*/
asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
parent);
foreach(lc, asplan->subplans)
{
SubPlan *sp = castNode(SubPlan, lfirst(lc));
SubPlanState *sps = ExecInitSubPlan(sp, parent);
asstate->subplans = lappend(asstate->subplans, sps);
parent->subPlan = lappend(parent->subPlan, sps);
}
/*
* Select the one to be used. For this, we need an estimate of the number
@ -1209,7 +1191,7 @@ ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent)
* Note: in future we might consider changing to different subplans on the
* fly, in case the original rowcount estimate turns out to be way off.
*/
static Datum
Datum
ExecAlternativeSubPlan(AlternativeSubPlanState *node,
ExprContext *econtext,
bool *isNull)

View File

@ -120,12 +120,8 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
subquerystate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) subquerystate);
subquerystate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) subquerystate);
subquerystate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) subquerystate);
/*
* tuple table initialization

View File

@ -139,12 +139,8 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, &scanstate->ss.ps);
/*
* tuple table initialization
@ -179,16 +175,16 @@ ExecInitTableFuncScan(TableFuncScan *node, EState *estate, int eflags)
scanstate->ns_names = tf->ns_names;
scanstate->ns_uris = (List *)
ExecInitExpr((Expr *) tf->ns_uris, (PlanState *) scanstate);
scanstate->ns_uris =
ExecInitExprList(tf->ns_uris, (PlanState *) scanstate);
scanstate->docexpr =
ExecInitExpr((Expr *) tf->docexpr, (PlanState *) scanstate);
scanstate->rowexpr =
ExecInitExpr((Expr *) tf->rowexpr, (PlanState *) scanstate);
scanstate->colexprs = (List *)
ExecInitExpr((Expr *) tf->colexprs, (PlanState *) scanstate);
scanstate->coldefexprs = (List *)
ExecInitExpr((Expr *) tf->coldefexprs, (PlanState *) scanstate);
scanstate->colexprs =
ExecInitExprList(tf->colexprs, (PlanState *) scanstate);
scanstate->coldefexprs =
ExecInitExprList(tf->coldefexprs, (PlanState *) scanstate);
scanstate->notnulls = tf->notnulls;

View File

@ -38,11 +38,85 @@
((Var *) (node))->varattno == SelfItemPointerAttributeNumber && \
((Var *) (node))->varlevelsup == 0)
static void TidListCreate(TidScanState *tidstate);
/* one element in tss_tidexprs */
typedef struct TidExpr
{
ExprState *exprstate; /* ExprState for a TID-yielding subexpr */
bool isarray; /* if true, it yields tid[] not just tid */
CurrentOfExpr *cexpr; /* alternatively, we can have CURRENT OF */
} TidExpr;
static void TidExprListCreate(TidScanState *tidstate);
static void TidListEval(TidScanState *tidstate);
static int itemptr_comparator(const void *a, const void *b);
static TupleTableSlot *TidNext(TidScanState *node);
/*
* Extract the qual subexpressions that yield TIDs to search for,
* and compile them into ExprStates if they're ordinary expressions.
*
* CURRENT OF is a special case that we can't compile usefully;
* just drop it into the TidExpr list as-is.
*/
static void
TidExprListCreate(TidScanState *tidstate)
{
TidScan *node = (TidScan *) tidstate->ss.ps.plan;
ListCell *l;
tidstate->tss_tidexprs = NIL;
tidstate->tss_isCurrentOf = false;
foreach(l, node->tidquals)
{
Expr *expr = (Expr *) lfirst(l);
TidExpr *tidexpr = (TidExpr *) palloc0(sizeof(TidExpr));
if (is_opclause(expr))
{
Node *arg1;
Node *arg2;
arg1 = get_leftop(expr);
arg2 = get_rightop(expr);
if (IsCTIDVar(arg1))
tidexpr->exprstate = ExecInitExpr((Expr *) arg2,
&tidstate->ss.ps);
else if (IsCTIDVar(arg2))
tidexpr->exprstate = ExecInitExpr((Expr *) arg1,
&tidstate->ss.ps);
else
elog(ERROR, "could not identify CTID variable");
tidexpr->isarray = false;
}
else if (expr && IsA(expr, ScalarArrayOpExpr))
{
ScalarArrayOpExpr *saex = (ScalarArrayOpExpr *) expr;
Assert(IsCTIDVar(linitial(saex->args)));
tidexpr->exprstate = ExecInitExpr(lsecond(saex->args),
&tidstate->ss.ps);
tidexpr->isarray = true;
}
else if (expr && IsA(expr, CurrentOfExpr))
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
tidexpr->cexpr = cexpr;
tidstate->tss_isCurrentOf = true;
}
else
elog(ERROR, "could not identify CTID expression");
tidstate->tss_tidexprs = lappend(tidstate->tss_tidexprs, tidexpr);
}
/* CurrentOfExpr could never appear OR'd with something else */
Assert(list_length(tidstate->tss_tidexprs) == 1 ||
!tidstate->tss_isCurrentOf);
}
/*
* Compute the list of TIDs to be visited, by evaluating the expressions
* for them.
@ -50,9 +124,8 @@ static TupleTableSlot *TidNext(TidScanState *node);
* (The result is actually an array, not a list.)
*/
static void
TidListCreate(TidScanState *tidstate)
TidListEval(TidScanState *tidstate)
{
List *evalList = tidstate->tss_tidquals;
ExprContext *econtext = tidstate->ss.ps.ps_ExprContext;
BlockNumber nblocks;
ItemPointerData *tidList;
@ -73,36 +146,21 @@ TidListCreate(TidScanState *tidstate)
* are simple OpExprs or CurrentOfExprs. If there are any
* ScalarArrayOpExprs, we may have to enlarge the array.
*/
numAllocTids = list_length(evalList);
numAllocTids = list_length(tidstate->tss_tidexprs);
tidList = (ItemPointerData *)
palloc(numAllocTids * sizeof(ItemPointerData));
numTids = 0;
tidstate->tss_isCurrentOf = false;
foreach(l, evalList)
foreach(l, tidstate->tss_tidexprs)
{
ExprState *exstate = (ExprState *) lfirst(l);
Expr *expr = exstate->expr;
TidExpr *tidexpr = (TidExpr *) lfirst(l);
ItemPointer itemptr;
bool isNull;
if (is_opclause(expr))
if (tidexpr->exprstate && !tidexpr->isarray)
{
FuncExprState *fexstate = (FuncExprState *) exstate;
Node *arg1;
Node *arg2;
arg1 = get_leftop(expr);
arg2 = get_rightop(expr);
if (IsCTIDVar(arg1))
exstate = (ExprState *) lsecond(fexstate->args);
else if (IsCTIDVar(arg2))
exstate = (ExprState *) linitial(fexstate->args);
else
elog(ERROR, "could not identify CTID variable");
itemptr = (ItemPointer)
DatumGetPointer(ExecEvalExprSwitchContext(exstate,
DatumGetPointer(ExecEvalExprSwitchContext(tidexpr->exprstate,
econtext,
&isNull));
if (!isNull &&
@ -119,9 +177,8 @@ TidListCreate(TidScanState *tidstate)
tidList[numTids++] = *itemptr;
}
}
else if (expr && IsA(expr, ScalarArrayOpExpr))
else if (tidexpr->exprstate && tidexpr->isarray)
{
ScalarArrayOpExprState *saexstate = (ScalarArrayOpExprState *) exstate;
Datum arraydatum;
ArrayType *itemarray;
Datum *ipdatums;
@ -129,8 +186,7 @@ TidListCreate(TidScanState *tidstate)
int ndatums;
int i;
exstate = (ExprState *) lsecond(saexstate->fxprstate.args);
arraydatum = ExecEvalExprSwitchContext(exstate,
arraydatum = ExecEvalExprSwitchContext(tidexpr->exprstate,
econtext,
&isNull);
if (isNull)
@ -159,12 +215,12 @@ TidListCreate(TidScanState *tidstate)
pfree(ipdatums);
pfree(ipnulls);
}
else if (expr && IsA(expr, CurrentOfExpr))
else
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) expr;
ItemPointerData cursor_tid;
if (execCurrentOf(cexpr, econtext,
Assert(tidexpr->cexpr);
if (execCurrentOf(tidexpr->cexpr, econtext,
RelationGetRelid(tidstate->ss.ss_currentRelation),
&cursor_tid))
{
@ -176,11 +232,8 @@ TidListCreate(TidScanState *tidstate)
numAllocTids * sizeof(ItemPointerData));
}
tidList[numTids++] = cursor_tid;
tidstate->tss_isCurrentOf = true;
}
}
else
elog(ERROR, "could not identify CTID expression");
}
/*
@ -272,11 +325,15 @@ TidNext(TidScanState *node)
* First time through, compute the list of TIDs to be visited
*/
if (node->tss_TidList == NULL)
TidListCreate(node);
TidListEval(node);
tidList = node->tss_TidList;
numTids = node->tss_NumTids;
/*
* We use node->tss_htup as the tuple pointer; note this can't just be a
* local variable here, as the scan tuple slot will keep a pointer to it.
*/
tuple = &(node->tss_htup);
/*
@ -470,16 +527,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
tidstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) tidstate);
tidstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) tidstate);
tidstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) tidstate);
tidstate->tss_tidquals = (List *)
ExecInitExpr((Expr *) node->tidquals,
(PlanState *) tidstate);
TidExprListCreate(tidstate);
/*
* tuple table initialization

View File

@ -120,7 +120,7 @@ ValuesNext(ValuesScanState *node)
* is a SubPlan, and there shouldn't be any (any subselects in the
* VALUES list should be InitPlans).
*/
exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
exprstatelist = ExecInitExprList(exprlist, NULL);
/* parser should have checked all sublists are the same length */
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
@ -242,12 +242,8 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* get info about values list

View File

@ -1826,16 +1826,12 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
winstate->temp_slot_1 = ExecInitExtraTupleSlot(estate);
winstate->temp_slot_2 = ExecInitExtraTupleSlot(estate);
winstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
(PlanState *) winstate);
/*
* WindowAgg nodes never have quals, since they can only occur at the
* logical top level of a query (ie, after any WHERE or HAVING filters)
*/
Assert(node->plan.qual == NIL);
winstate->ss.ps.qual = NIL;
winstate->ss.ps.qual = NULL;
/*
* initialize child nodes
@ -1894,7 +1890,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags)
foreach(l, winstate->funcs)
{
WindowFuncExprState *wfuncstate = (WindowFuncExprState *) lfirst(l);
WindowFunc *wfunc = (WindowFunc *) wfuncstate->xprstate.expr;
WindowFunc *wfunc = wfuncstate->wfunc;
WindowStatePerFunc perfuncstate;
AclResult aclresult;
int i;

View File

@ -156,12 +156,8 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags)
/*
* initialize child expressions
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
(PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
(PlanState *) scanstate);
scanstate->ss.ps.qual =
ExecInitQual(node->scan.plan.qual, (PlanState *) scanstate);
/*
* tuple table initialization

View File

@ -3501,7 +3501,7 @@ cost_qual_eval_walker(Node *node, cost_qual_eval_context *context)
/*
* Aggref and WindowFunc nodes are (and should be) treated like Vars,
* ie, zero execution cost in the current model, because they behave
* essentially like Vars in execQual.c. We disregard the costs of
* essentially like Vars at execution. We disregard the costs of
* their input expressions for the same reason. The actual execution
* costs of the aggregate/window functions and their arguments have to
* be factored into plan-node-specific costing of the Agg or WindowAgg

View File

@ -5013,7 +5013,7 @@ make_pathkeys_for_window(PlannerInfo *root, WindowClause *wc,
* bloat the sort dataset, and because it might cause unexpected output order
* if the sort isn't stable. However there's a constraint on that: all SRFs
* in the tlist should be evaluated at the same plan step, so that they can
* run in sync in ExecTargetList. So if any SRFs are in sort columns, we
* run in sync in nodeProjectSet. So if any SRFs are in sort columns, we
* mustn't postpone any SRFs. (Note that in principle that policy should
* probably get applied to the group/window input targetlists too, but we
* have not done that historically.) Lastly, expensive expressions are

View File

@ -3395,7 +3395,7 @@ eval_const_expressions_mutator(Node *node,
* Else, make a scalar (argisrow == false) NullTest
* for this field. Scalar semantics are required
* because IS [NOT] NULL doesn't recurse; see comments
* in ExecEvalNullTest().
* in ExecEvalRowNullInt().
*/
newntest = makeNode(NullTest);
newntest->arg = (Expr *) relem;
@ -3539,8 +3539,8 @@ eval_const_expressions_mutator(Node *node,
* FALSE: drop (does not affect result)
* TRUE: force result to TRUE
* NULL: keep only one
* We must keep one NULL input because ExecEvalOr returns NULL when no input
* is TRUE and at least one is NULL. We don't actually include the NULL
* We must keep one NULL input because OR expressions evaluate to NULL when no
* input is TRUE and at least one is NULL. We don't actually include the NULL
* here, that's supposed to be done by the caller.
*
* The output arguments *haveNull and *forceTrue must be initialized FALSE
@ -3651,9 +3651,9 @@ simplify_or_arguments(List *args,
* TRUE: drop (does not affect result)
* FALSE: force result to FALSE
* NULL: keep only one
* We must keep one NULL input because ExecEvalAnd returns NULL when no input
* is FALSE and at least one is NULL. We don't actually include the NULL
* here, that's supposed to be done by the caller.
* We must keep one NULL input because AND expressions evaluate to NULL when
* no input is FALSE and at least one is NULL. We don't actually include the
* NULL here, that's supposed to be done by the caller.
*
* The output arguments *haveNull and *forceFalse must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or false constant,

View File

@ -107,7 +107,7 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
fmgr_info_cxt(my_extra->typiofunc, &my_extra->proc, mcxt);
/* Look up constraints for domain */
InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt);
InitDomainConstraintRef(domainType, &my_extra->constraint_ref, mcxt, true);
/* We don't make an ExprContext until needed */
my_extra->econtext = NULL;
@ -122,7 +122,9 @@ domain_state_setup(Oid domainType, bool binary, MemoryContext mcxt)
/*
* domain_check_input - apply the cached checks.
*
* This is extremely similar to ExecEvalCoerceToDomain in execQual.c.
* This is roughly similar to the handling of CoerceToDomain nodes in
* execExpr*.c, but we execute each constraint separately, rather than
* compiling them in-line within a larger expression.
*/
static void
domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
@ -149,9 +151,6 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
break;
case DOM_CONSTRAINT_CHECK:
{
Datum conResult;
bool conIsNull;
/* Make the econtext if we didn't already */
if (econtext == NULL)
{
@ -165,24 +164,20 @@ domain_check_input(Datum value, bool isnull, DomainIOData *my_extra)
/*
* Set up value to be returned by CoerceToDomainValue
* nodes. Unlike ExecEvalCoerceToDomain, this econtext
* couldn't be shared with anything else, so no need to
* save and restore fields. But we do need to protect the
* passed-in value against being changed by called
* functions. (It couldn't be a R/W expanded object for
* most uses, but that seems possible for domain_check().)
* nodes. Unlike in the generic expression case, this
* econtext couldn't be shared with anything else, so no
* need to save and restore fields. But we do need to
* protect the passed-in value against being changed by
* called functions. (It couldn't be a R/W expanded
* object for most uses, but that seems possible for
* domain_check().)
*/
econtext->domainValue_datum =
MakeExpandedObjectReadOnly(value, isnull,
my_extra->constraint_ref.tcache->typlen);
econtext->domainValue_isNull = isnull;
conResult = ExecEvalExprSwitchContext(con->check_expr,
econtext,
&conIsNull);
if (!conIsNull &&
!DatumGetBool(conResult))
if (!ExecCheck(con->check_exprstate, econtext))
ereport(ERROR,
(errcode(ERRCODE_CHECK_VIOLATION),
errmsg("value for domain %s violates check constraint \"%s\"",

View File

@ -7000,7 +7000,7 @@ find_param_referent(Param *param, deparse_context *context,
foreach(lc2, ps->subPlan)
{
SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
SubPlan *subplan = (SubPlan *) sstate->xprstate.expr;
SubPlan *subplan = sstate->subplan;
ListCell *lc3;
ListCell *lc4;
@ -7041,7 +7041,7 @@ find_param_referent(Param *param, deparse_context *context,
continue;
/* No parameters to be had here. */
Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
Assert(sstate->subplan->parParam == NIL);
/* Keep looking, but we are emerging from an initplan. */
in_same_plan_level = false;

View File

@ -72,7 +72,6 @@
#include "catalog/pg_class.h"
#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "executor/executor.h"
#include "executor/spi.h"
#include "executor/tablefunc.h"
#include "fmgr.h"
@ -620,10 +619,11 @@ xmltotext_with_xmloption(xmltype *data, XmlOptionType xmloption_arg)
xmltype *
xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
xmlelement(XmlExpr *xexpr,
Datum *named_argvalue, bool *named_argnull,
Datum *argvalue, bool *argnull)
{
#ifdef USE_LIBXML
XmlExpr *xexpr = (XmlExpr *) xmlExpr->xprstate.expr;
xmltype *result;
List *named_arg_strings;
List *arg_strings;
@ -635,48 +635,47 @@ xmlelement(XmlExprState *xmlExpr, ExprContext *econtext)
volatile xmlTextWriterPtr writer = NULL;
/*
* We first evaluate all the arguments, then start up libxml and create
* the result. This avoids issues if one of the arguments involves a call
* to some other function or subsystem that wants to use libxml on its own
* terms.
* All arguments are already evaluated, and their values are passed in the
* named_argvalue/named_argnull or argvalue/argnull arrays. This avoids
* issues if one of the arguments involves a call to some other function
* or subsystem that wants to use libxml on its own terms. We examine the
* original XmlExpr to identify the numbers and types of the arguments.
*/
named_arg_strings = NIL;
i = 0;
foreach(arg, xmlExpr->named_args)
foreach(arg, xexpr->named_args)
{
ExprState *e = (ExprState *) lfirst(arg);
Datum value;
bool isnull;
Expr *e = (Expr *) lfirst(arg);
char *str;
value = ExecEvalExpr(e, econtext, &isnull);
if (isnull)
if (named_argnull[i])
str = NULL;
else
str = map_sql_value_to_xml_value(value, exprType((Node *) e->expr), false);
str = map_sql_value_to_xml_value(named_argvalue[i],
exprType((Node *) e),
false);
named_arg_strings = lappend(named_arg_strings, str);
i++;
}
arg_strings = NIL;
foreach(arg, xmlExpr->args)
i = 0;
foreach(arg, xexpr->args)
{
ExprState *e = (ExprState *) lfirst(arg);
Datum value;
bool isnull;
Expr *e = (Expr *) lfirst(arg);
char *str;
value = ExecEvalExpr(e, econtext, &isnull);
/* here we can just forget NULL elements immediately */
if (!isnull)
if (!argnull[i])
{
str = map_sql_value_to_xml_value(value,
exprType((Node *) e->expr), true);
str = map_sql_value_to_xml_value(argvalue[i],
exprType((Node *) e),
true);
arg_strings = lappend(arg_strings, str);
}
i++;
}
/* now safe to run libxml */
xmlerrcxt = pg_xml_init(PG_XML_STRICTNESS_ALL);
PG_TRY();

View File

@ -96,11 +96,11 @@ static TypeCacheEntry *firstDomainTypeEntry = NULL;
* this struct for the common case of a constraint-less domain; we just set
* domainData to NULL to indicate that.
*
* Within a DomainConstraintCache, we abuse the DomainConstraintState node
* type a bit: check_expr fields point to expression plan trees, not plan
* state trees. When needed, expression state trees are built by flat-copying
* the DomainConstraintState nodes and applying ExecInitExpr to check_expr.
* Such a state tree is not part of the DomainConstraintCache, but is
* Within a DomainConstraintCache, we store expression plan trees, but the
* check_exprstate fields of the DomainConstraintState nodes are just NULL.
* When needed, expression evaluation nodes are built by flat-copying the
* DomainConstraintState nodes and applying ExecInitExpr to check_expr.
* Such a node tree is not part of the DomainConstraintCache, but is
* considered to belong to a DomainConstraintRef.
*/
struct DomainConstraintCache
@ -779,8 +779,8 @@ load_domaintype_info(TypeCacheEntry *typentry)
r = makeNode(DomainConstraintState);
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
/* Must cast here because we're not storing an expr state node */
r->check_expr = (ExprState *) check_expr;
r->check_expr = check_expr;
r->check_exprstate = NULL;
MemoryContextSwitchTo(oldcxt);
@ -859,6 +859,7 @@ load_domaintype_info(TypeCacheEntry *typentry)
r->constrainttype = DOM_CONSTRAINT_NOTNULL;
r->name = pstrdup("NOT NULL");
r->check_expr = NULL;
r->check_exprstate = NULL;
/* lcons to apply the nullness check FIRST */
dcc->constraints = lcons(r, dcc->constraints);
@ -946,8 +947,8 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
newr = makeNode(DomainConstraintState);
newr->constrainttype = r->constrainttype;
newr->name = r->name;
/* Must cast here because cache items contain expr plan trees */
newr->check_expr = ExecInitExpr((Expr *) r->check_expr, NULL);
newr->check_expr = r->check_expr;
newr->check_exprstate = ExecInitExpr(r->check_expr, NULL);
result = lappend(result, newr);
}
@ -962,13 +963,18 @@ prep_domain_constraints(List *constraints, MemoryContext execctx)
*
* Caller must tell us the MemoryContext in which the DomainConstraintRef
* lives. The ref will be cleaned up when that context is reset/deleted.
*
* Caller must also tell us whether it wants check_exprstate fields to be
* computed in the DomainConstraintState nodes attached to this ref.
* If it doesn't, we need not make a copy of the DomainConstraintState list.
*/
void
InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
MemoryContext refctx)
MemoryContext refctx, bool need_exprstate)
{
/* Look up the typcache entry --- we assume it survives indefinitely */
ref->tcache = lookup_type_cache(type_id, TYPECACHE_DOMAIN_INFO);
ref->need_exprstate = need_exprstate;
/* For safety, establish the callback before acquiring a refcount */
ref->refctx = refctx;
ref->dcc = NULL;
@ -980,8 +986,11 @@ InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
{
ref->dcc = ref->tcache->domainData;
ref->dcc->dccRefCount++;
ref->constraints = prep_domain_constraints(ref->dcc->constraints,
ref->refctx);
if (ref->need_exprstate)
ref->constraints = prep_domain_constraints(ref->dcc->constraints,
ref->refctx);
else
ref->constraints = ref->dcc->constraints;
}
else
ref->constraints = NIL;
@ -1032,8 +1041,11 @@ UpdateDomainConstraintRef(DomainConstraintRef *ref)
{
ref->dcc = dcc;
dcc->dccRefCount++;
ref->constraints = prep_domain_constraints(dcc->constraints,
ref->refctx);
if (ref->need_exprstate)
ref->constraints = prep_domain_constraints(dcc->constraints,
ref->refctx);
else
ref->constraints = dcc->constraints;
}
}
}

View File

@ -0,0 +1,642 @@
/*-------------------------------------------------------------------------
*
* execExpr.h
* Low level infrastructure related to expression evaluation
*
*
* Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* src/include/executor/execExpr.h
*
*-------------------------------------------------------------------------
*/
#ifndef EXEC_EXPR_H
#define EXEC_EXPR_H
#include "nodes/execnodes.h"
/* forward reference to avoid circularity */
struct ArrayRefState;
/* Bits in ExprState->flags (see also execnodes.h for public flag bits): */
/* expression's interpreter has been initialized */
#define EEO_FLAG_INTERPRETER_INITIALIZED (1 << 1)
/* jump-threading is in use */
#define EEO_FLAG_DIRECT_THREADED (1 << 2)
/*
* Discriminator for ExprEvalSteps.
*
* Identifies the operation to be executed and which member in the
* ExprEvalStep->d union is valid.
*
* The order of entries needs to be kept in sync with the dispatch_table[]
* array in execExprInterp.c:ExecInterpExpr().
*/
typedef enum ExprEvalOp
{
/* entire expression has been evaluated completely, return */
EEOP_DONE,
/* apply slot_getsomeattrs on corresponding tuple slot */
EEOP_INNER_FETCHSOME,
EEOP_OUTER_FETCHSOME,
EEOP_SCAN_FETCHSOME,
/* compute non-system Var value */
/* "FIRST" variants are used only the first time through */
EEOP_INNER_VAR_FIRST,
EEOP_INNER_VAR,
EEOP_OUTER_VAR_FIRST,
EEOP_OUTER_VAR,
EEOP_SCAN_VAR_FIRST,
EEOP_SCAN_VAR,
/* compute system Var value */
EEOP_INNER_SYSVAR,
EEOP_OUTER_SYSVAR,
EEOP_SCAN_SYSVAR,
/* compute wholerow Var */
EEOP_WHOLEROW,
/* compute non-system Var value, assign it into ExprState's resultslot */
/* (these are not used if _FIRST checks would be needed) */
EEOP_ASSIGN_INNER_VAR,
EEOP_ASSIGN_OUTER_VAR,
EEOP_ASSIGN_SCAN_VAR,
/* assign ExprState's resvalue/resnull to a column of its resultslot */
EEOP_ASSIGN_TMP,
/* ditto, applying MakeExpandedObjectReadOnly() */
EEOP_ASSIGN_TMP_MAKE_RO,
/* evaluate Const value */
EEOP_CONST,
/*
* Evaluate function call (including OpExprs etc). For speed, we
* distinguish in the opcode whether the function is strict and/or
* requires usage stats tracking.
*/
EEOP_FUNCEXPR,
EEOP_FUNCEXPR_STRICT,
EEOP_FUNCEXPR_FUSAGE,
EEOP_FUNCEXPR_STRICT_FUSAGE,
/*
* Evaluate boolean AND expression, one step per subexpression. FIRST/LAST
* subexpressions are special-cased for performance. Since AND always has
* at least two subexpressions, FIRST and LAST never apply to the same
* subexpression.
*/
EEOP_BOOL_AND_STEP_FIRST,
EEOP_BOOL_AND_STEP,
EEOP_BOOL_AND_STEP_LAST,
/* similarly for boolean OR expression */
EEOP_BOOL_OR_STEP_FIRST,
EEOP_BOOL_OR_STEP,
EEOP_BOOL_OR_STEP_LAST,
/* evaluate boolean NOT expression */
EEOP_BOOL_NOT_STEP,
/* simplified version of BOOL_AND_STEP for use by ExecQual() */
EEOP_QUAL,
/* unconditional jump to another step */
EEOP_JUMP,
/* conditional jumps based on current result value */
EEOP_JUMP_IF_NULL,
EEOP_JUMP_IF_NOT_NULL,
EEOP_JUMP_IF_NOT_TRUE,
/* perform NULL tests for scalar values */
EEOP_NULLTEST_ISNULL,
EEOP_NULLTEST_ISNOTNULL,
/* perform NULL tests for row values */
EEOP_NULLTEST_ROWISNULL,
EEOP_NULLTEST_ROWISNOTNULL,
/* evaluate a BooleanTest expression */
EEOP_BOOLTEST_IS_TRUE,
EEOP_BOOLTEST_IS_NOT_TRUE,
EEOP_BOOLTEST_IS_FALSE,
EEOP_BOOLTEST_IS_NOT_FALSE,
/* evaluate PARAM_EXEC/EXTERN parameters */
EEOP_PARAM_EXEC,
EEOP_PARAM_EXTERN,
/* return CaseTestExpr value */
EEOP_CASE_TESTVAL,
/* apply MakeExpandedObjectReadOnly() to target value */
EEOP_MAKE_READONLY,
/* evaluate assorted special-purpose expression types */
EEOP_IOCOERCE,
EEOP_DISTINCT,
EEOP_NULLIF,
EEOP_SQLVALUEFUNCTION,
EEOP_CURRENTOFEXPR,
EEOP_ARRAYEXPR,
EEOP_ARRAYCOERCE,
EEOP_ROW,
/*
* Compare two individual elements of each of two compared ROW()
* expressions. Skip to ROWCOMPARE_FINAL if elements are not equal.
*/
EEOP_ROWCOMPARE_STEP,
/* evaluate boolean value based on previous ROWCOMPARE_STEP operations */
EEOP_ROWCOMPARE_FINAL,
/* evaluate GREATEST() or LEAST() */
EEOP_MINMAX,
/* evaluate FieldSelect expression */
EEOP_FIELDSELECT,
/*
* Deform tuple before evaluating new values for individual fields in a
* FieldStore expression.
*/
EEOP_FIELDSTORE_DEFORM,
/*
* Form the new tuple for a FieldStore expression. Individual fields will
* have been evaluated into columns of the tuple deformed by the preceding
* DEFORM step.
*/
EEOP_FIELDSTORE_FORM,
/* Process an array subscript; short-circuit expression to NULL if NULL */
EEOP_ARRAYREF_SUBSCRIPT,
/*
* Compute old array element/slice when an ArrayRef assignment expression
* contains ArrayRef/FieldStore subexpressions. Value is accessed using
* the CaseTest mechanism.
*/
EEOP_ARRAYREF_OLD,
/* compute new value for ArrayRef assignment expression */
EEOP_ARRAYREF_ASSIGN,
/* compute element/slice for ArrayRef fetch expression */
EEOP_ARRAYREF_FETCH,
/* evaluate value for CoerceToDomainValue */
EEOP_DOMAIN_TESTVAL,
/* evaluate a domain's NOT NULL constraint */
EEOP_DOMAIN_NOTNULL,
/* evaluate a single domain CHECK constraint */
EEOP_DOMAIN_CHECK,
/* evaluate assorted special-purpose expression types */
EEOP_CONVERT_ROWTYPE,
EEOP_SCALARARRAYOP,
EEOP_XMLEXPR,
EEOP_AGGREF,
EEOP_GROUPING_FUNC,
EEOP_WINDOW_FUNC,
EEOP_SUBPLAN,
EEOP_ALTERNATIVE_SUBPLAN,
/* non-existent operation, used e.g. to check array lengths */
EEOP_LAST
} ExprEvalOp;
typedef struct ExprEvalStep
{
/*
* Instruction to be executed. During instruction preparation this is an
* enum ExprEvalOp, but later it can be changed to some other type, e.g. a
* pointer for computed goto (that's why it's an intptr_t).
*/
intptr_t opcode;
/* where to store the result of this step */
Datum *resvalue;
bool *resnull;
/*
* Inline data for the operation. Inline data is faster to access, but
* also bloats the size of all instructions. The union should be kept to
* no more than 40 bytes on 64-bit systems (so that the entire struct is
* no more than 64 bytes, a single cacheline on common systems).
*/
union
{
/* for EEOP_INNER/OUTER/SCAN_FETCHSOME */
struct
{
/* attribute number up to which to fetch (inclusive) */
int last_var;
} fetch;
/* for EEOP_INNER/OUTER/SCAN_[SYS]VAR[_FIRST] */
struct
{
/* attnum is attr number - 1 for regular VAR ... */
/* but it's just the normal (negative) attr number for SYSVAR */
int attnum;
Oid vartype; /* type OID of variable */
} var;
/* for EEOP_WHOLEROW */
struct
{
Var *var; /* original Var node in plan tree */
bool first; /* first time through, need to initialize? */
bool slow; /* need runtime check for nulls? */
TupleDesc tupdesc; /* descriptor for resulting tuples */
JunkFilter *junkFilter; /* JunkFilter to remove resjunk cols */
} wholerow;
/* for EEOP_ASSIGN_*_VAR */
struct
{
/* target index in ExprState->resultslot->tts_values/nulls */
int resultnum;
/* source attribute number - 1 */
int attnum;
} assign_var;
/* for EEOP_ASSIGN_TMP[_MAKE_RO] */
struct
{
/* target index in ExprState->resultslot->tts_values/nulls */
int resultnum;
} assign_tmp;
/* for EEOP_CONST */
struct
{
/* constant's value */
Datum value;
bool isnull;
} constval;
/* for EEOP_FUNCEXPR_* / NULLIF / DISTINCT */
struct
{
FmgrInfo *finfo; /* function's lookup data */
FunctionCallInfo fcinfo_data; /* arguments etc */
/* faster to access without additional indirection: */
PGFunction fn_addr; /* actual call address */
int nargs; /* number of arguments */
} func;
/* for EEOP_BOOL_*_STEP */
struct
{
bool *anynull; /* track if any input was NULL */
int jumpdone; /* jump here if result determined */
} boolexpr;
/* for EEOP_QUAL */
struct
{
int jumpdone; /* jump here on false or null */
} qualexpr;
/* for EEOP_JUMP[_CONDITION] */
struct
{
int jumpdone; /* target instruction's index */
} jump;
/* for EEOP_NULLTEST_ROWIS[NOT]NULL */
struct
{
/* cached tupdesc pointer - filled at runtime */
TupleDesc argdesc;
} nulltest_row;
/* for EEOP_PARAM_EXEC/EXTERN */
struct
{
int paramid; /* numeric ID for parameter */
Oid paramtype; /* OID of parameter's datatype */
} param;
/* for EEOP_CASE_TESTVAL/DOMAIN_TESTVAL */
struct
{
Datum *value; /* value to return */
bool *isnull;
} casetest;
/* for EEOP_MAKE_READONLY */
struct
{
Datum *value; /* value to coerce to read-only */
bool *isnull;
} make_readonly;
/* for EEOP_IOCOERCE */
struct
{
/* lookup and call info for source type's output function */
FmgrInfo *finfo_out;
FunctionCallInfo fcinfo_data_out;
/* lookup and call info for result type's input function */
FmgrInfo *finfo_in;
FunctionCallInfo fcinfo_data_in;
} iocoerce;
/* for EEOP_SQLVALUEFUNCTION */
struct
{
SQLValueFunction *svf;
} sqlvaluefunction;
/* for EEOP_ARRAYEXPR */
struct
{
Datum *elemvalues; /* element values get stored here */
bool *elemnulls;
int nelems; /* length of the above arrays */
Oid elemtype; /* array element type */
int16 elemlength; /* typlen of the array element type */
bool elembyval; /* is the element type pass-by-value? */
char elemalign; /* typalign of the element type */
bool multidims; /* is array expression multi-D? */
} arrayexpr;
/* for EEOP_ARRAYCOERCE */
struct
{
ArrayCoerceExpr *coerceexpr;
Oid resultelemtype; /* element type of result array */
FmgrInfo *elemfunc; /* lookup info for element coercion
* function */
struct ArrayMapState *amstate; /* workspace for array_map */
} arraycoerce;
/* for EEOP_ROW */
struct
{
TupleDesc tupdesc; /* descriptor for result tuples */
/* workspace for the values constituting the row: */
Datum *elemvalues;
bool *elemnulls;
} row;
/* for EEOP_ROWCOMPARE_STEP */
struct
{
/* lookup and call data for column comparison function */
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
PGFunction fn_addr;
/* target for comparison resulting in NULL */
int jumpnull;
/* target for comparison yielding inequality */
int jumpdone;
} rowcompare_step;
/* for EEOP_ROWCOMPARE_FINAL */
struct
{
RowCompareType rctype;
} rowcompare_final;
/* for EEOP_MINMAX */
struct
{
/* workspace for argument values */
Datum *values;
bool *nulls;
int nelems;
/* is it GREATEST or LEAST? */
MinMaxOp op;
/* lookup and call data for comparison function */
FmgrInfo *finfo;
FunctionCallInfo fcinfo_data;
} minmax;
/* for EEOP_FIELDSELECT */
struct
{
AttrNumber fieldnum; /* field number to extract */
Oid resulttype; /* field's type */
/* cached tupdesc pointer - filled at runtime */
TupleDesc argdesc;
} fieldselect;
/* for EEOP_FIELDSTORE_DEFORM / FIELDSTORE_FORM */
struct
{
/* original expression node */
FieldStore *fstore;
/* cached tupdesc pointer - filled at runtime */
/* note that a DEFORM and FORM pair share the same tupdesc */
TupleDesc *argdesc;
/* workspace for column values */
Datum *values;
bool *nulls;
int ncolumns;
} fieldstore;
/* for EEOP_ARRAYREF_SUBSCRIPT */
struct
{
/* too big to have inline */
struct ArrayRefState *state;
int off; /* 0-based index of this subscript */
bool isupper; /* is it upper or lower subscript? */
int jumpdone; /* jump here on null */
} arrayref_subscript;
/* for EEOP_ARRAYREF_OLD / ASSIGN / FETCH */
struct
{
/* too big to have inline */
struct ArrayRefState *state;
} arrayref;
/* for EEOP_DOMAIN_NOTNULL / DOMAIN_CHECK */
struct
{
/* name of constraint */
char *constraintname;
/* where the result of a CHECK constraint will be stored */
Datum *checkvalue;
bool *checknull;
/* OID of domain type */
Oid resulttype;
} domaincheck;
/* for EEOP_CONVERT_ROWTYPE */
struct
{
ConvertRowtypeExpr *convert; /* original expression */
/* these three fields are filled at runtime: */
TupleDesc indesc; /* tupdesc for input type */
TupleDesc outdesc; /* tupdesc for output type */
TupleConversionMap *map; /* column mapping */
bool initialized; /* initialized for current types? */
} convert_rowtype;
/* for EEOP_SCALARARRAYOP */
struct
{
/* element_type/typlen/typbyval/typalign are filled at runtime */
Oid element_type; /* InvalidOid if not yet filled */
bool useOr; /* use OR or AND semantics? */
int16 typlen; /* array element type storage info */
bool typbyval;
char typalign;
FmgrInfo *finfo; /* function's lookup data */
FunctionCallInfo fcinfo_data; /* arguments etc */
/* faster to access without additional indirection: */
PGFunction fn_addr; /* actual call address */
} scalararrayop;
/* for EEOP_XMLEXPR */
struct
{
XmlExpr *xexpr; /* original expression node */
/* workspace for evaluating named args, if any */
Datum *named_argvalue;
bool *named_argnull;
/* workspace for evaluating unnamed args, if any */
Datum *argvalue;
bool *argnull;
} xmlexpr;
/* for EEOP_AGGREF */
struct
{
/* out-of-line state, modified by nodeAgg.c */
AggrefExprState *astate;
} aggref;
/* for EEOP_GROUPING_FUNC */
struct
{
AggState *parent; /* parent Agg */
List *clauses; /* integer list of column numbers */
} grouping_func;
/* for EEOP_WINDOW_FUNC */
struct
{
/* out-of-line state, modified by nodeWindowFunc.c */
WindowFuncExprState *wfstate;
} window_func;
/* for EEOP_SUBPLAN */
struct
{
/* out-of-line state, created by nodeSubplan.c */
SubPlanState *sstate;
} subplan;
/* for EEOP_ALTERNATIVE_SUBPLAN */
struct
{
/* out-of-line state, created by nodeSubplan.c */
AlternativeSubPlanState *asstate;
} alternative_subplan;
} d;
} ExprEvalStep;
/* Non-inline data for array operations */
typedef struct ArrayRefState
{
bool isassignment; /* is it assignment, or just fetch? */
Oid refelemtype; /* OID of the array element type */
int16 refattrlength; /* typlen of array type */
int16 refelemlength; /* typlen of the array element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
/* numupper and upperprovided[] are filled at compile time */
/* at runtime, extracted subscript datums get stored in upperindex[] */
int numupper;
bool upperprovided[MAXDIM];
int upperindex[MAXDIM];
/* similarly for lower indexes, if any */
int numlower;
bool lowerprovided[MAXDIM];
int lowerindex[MAXDIM];
/* subscript expressions get evaluated into here */
Datum subscriptvalue;
bool subscriptnull;
/* for assignment, new value to assign is evaluated into here */
Datum replacevalue;
bool replacenull;
/* if we have a nested assignment, ARRAYREF_OLD puts old value here */
Datum prevvalue;
bool prevnull;
} ArrayRefState;
extern void ExecReadyInterpretedExpr(ExprState *state);
extern ExprEvalOp ExecEvalStepOp(ExprState *state, ExprEvalStep *op);
/*
* Non fast-path execution functions. These are externs instead of statics in
* execExprInterp.c, because that allows them to be used by other methods of
* expression evaluation, reducing code duplication.
*/
extern void ExecEvalParamExec(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalParamExtern(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalSQLValueFunction(ExprState *state, ExprEvalStep *op);
extern void ExecEvalCurrentOfExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalRowNull(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalRowNotNull(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalArrayExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalArrayCoerce(ExprState *state, ExprEvalStep *op);
extern void ExecEvalRow(ExprState *state, ExprEvalStep *op);
extern void ExecEvalMinMax(ExprState *state, ExprEvalStep *op);
extern void ExecEvalFieldSelect(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalFieldStoreDeForm(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalFieldStoreForm(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern bool ExecEvalArrayRefSubscript(ExprState *state, ExprEvalStep *op);
extern void ExecEvalArrayRefFetch(ExprState *state, ExprEvalStep *op);
extern void ExecEvalArrayRefOld(ExprState *state, ExprEvalStep *op);
extern void ExecEvalArrayRefAssign(ExprState *state, ExprEvalStep *op);
extern void ExecEvalConvertRowtype(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalScalarArrayOp(ExprState *state, ExprEvalStep *op);
extern void ExecEvalConstraintNotNull(ExprState *state, ExprEvalStep *op);
extern void ExecEvalConstraintCheck(ExprState *state, ExprEvalStep *op);
extern void ExecEvalXmlExpr(ExprState *state, ExprEvalStep *op);
extern void ExecEvalGroupingFunc(ExprState *state, ExprEvalStep *op);
extern void ExecEvalSubPlan(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalAlternativeSubPlan(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
extern void ExecEvalWholeRowVar(ExprState *state, ExprEvalStep *op,
ExprContext *econtext);
#endif /* EXEC_EXPR_H */

View File

@ -37,13 +37,6 @@
#undef EXEC_NESTLOOPDEBUG
*/
/* ----------------
* EXEC_EVALDEBUG is a flag which turns on debugging of
* ExecEval and ExecTargetList() stuff by EV_printf() in execQual.c
* ----------------
#undef EXEC_EVALDEBUG
*/
/* ----------------
* EXEC_SORTDEBUG is a flag which turns on debugging of
* the ExecSort() stuff by SO_printf() in nodeSort.c
@ -85,20 +78,6 @@
#define ENL1_printf(message)
#endif /* EXEC_NESTLOOPDEBUG */
/* ----------------
* exec eval / target list debugging defines
* ----------------
*/
#ifdef EXEC_EVALDEBUG
#define EV_nodeDisplay(l) nodeDisplay(l)
#define EV_printf(s) printf(s)
#define EV1_printf(s, a) printf(s, a)
#else
#define EV_nodeDisplay(l)
#define EV_printf(s)
#define EV1_printf(s, a)
#endif /* EXEC_EVALDEBUG */
/* ----------------
* sort node debugging defines
* ----------------
@ -146,4 +125,4 @@
#define MJ_DEBUG_PROC_NODE(slot)
#endif /* EXEC_MERGEJOINDEBUG */
#endif /* ExecDebugIncluded */
#endif /* EXECDEBUG_H */

View File

@ -65,15 +65,6 @@
#define EXEC_FLAG_WITH_NO_DATA 0x0080 /* rel scannability doesn't matter */
/*
* ExecEvalExpr was formerly a function containing a switch statement;
* now it's just a macro invoking the function pointed to by an ExprState
* node. Beware of double evaluation of the ExprState argument!
*/
#define ExecEvalExpr(expr, econtext, isNull) \
((*(expr)->evalfunc) (expr, econtext, isNull))
/* Hook for plugins to get control in ExecutorStart() */
typedef void (*ExecutorStart_hook_type) (QueryDesc *queryDesc, int eflags);
extern PGDLLIMPORT ExecutorStart_hook_type ExecutorStart_hook;
@ -242,29 +233,155 @@ extern void ExecEndNode(PlanState *node);
extern bool ExecShutdownNode(PlanState *node);
/*
* prototypes from functions in execQual.c
* prototypes from functions in execExpr.c
*/
extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
bool *isNull);
extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
bool *isNull);
extern Tuplestorestate *ExecMakeTableFunctionResult(ExprState *funcexpr,
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
extern ExprState *ExecInitQual(List *qual, PlanState *parent);
extern ExprState *ExecInitCheck(List *qual, PlanState *parent);
extern List *ExecInitExprList(List *nodes, PlanState *parent);
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot,
PlanState *parent,
TupleDesc inputDesc);
extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
extern ExprState *ExecPrepareQual(List *qual, EState *estate);
extern ExprState *ExecPrepareCheck(List *qual, EState *estate);
extern List *ExecPrepareExprList(List *nodes, EState *estate);
/*
* ExecEvalExpr
*
* 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.
*
* The caller should already have switched into the temporary memory
* context econtext->ecxt_per_tuple_memory. The convenience entry point
* ExecEvalExprSwitchContext() is provided for callers who don't prefer to
* do the switch in an outer loop.
*/
#ifndef FRONTEND
static inline Datum
ExecEvalExpr(ExprState *state,
ExprContext *econtext,
bool *isNull)
{
return (*state->evalfunc) (state, econtext, isNull);
}
#endif
/*
* ExecEvalExprSwitchContext
*
* Same as ExecEvalExpr, but get into the right allocation context explicitly.
*/
#ifndef FRONTEND
static inline Datum
ExecEvalExprSwitchContext(ExprState *state,
ExprContext *econtext,
bool *isNull)
{
Datum retDatum;
MemoryContext oldContext;
oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
retDatum = (*state->evalfunc) (state, econtext, isNull);
MemoryContextSwitchTo(oldContext);
return retDatum;
}
#endif
/*
* ExecProject
*
* Projects a tuple based on projection info and stores it in the slot passed
* to ExecBuildProjectInfo().
*
* Note: the result is always a virtual tuple; therefore it may reference
* the contents of the exprContext's scan tuples and/or temporary results
* constructed in the exprContext. If the caller wishes the result to be
* valid longer than that data will be valid, he must call ExecMaterializeSlot
* on the result slot.
*/
#ifndef FRONTEND
static inline TupleTableSlot *
ExecProject(ProjectionInfo *projInfo)
{
ExprContext *econtext = projInfo->pi_exprContext;
ExprState *state = &projInfo->pi_state;
TupleTableSlot *slot = state->resultslot;
bool isnull;
/*
* Clear any former contents of the result slot. This makes it safe for
* us to use the slot's Datum/isnull arrays as workspace.
*/
ExecClearTuple(slot);
/* Run the expression, discarding scalar result from the last column. */
(void) ExecEvalExprSwitchContext(state, econtext, &isnull);
/*
* Successfully formed a result row. Mark the result slot as containing a
* valid virtual tuple (inlined version of ExecStoreVirtualTuple()).
*/
slot->tts_isempty = false;
slot->tts_nvalid = slot->tts_tupleDescriptor->natts;
return slot;
}
#endif
/*
* ExecQual - evaluate a qual prepared with ExecInitQual (possibly via
* ExecPrepareQual). Returns true if qual is satisfied, else false.
*
* Note: ExecQual used to have a third argument "resultForNull". The
* behavior of this function now corresponds to resultForNull == false.
* If you want the resultForNull == true behavior, see ExecCheck.
*/
#ifndef FRONTEND
static inline bool
ExecQual(ExprState *state, ExprContext *econtext)
{
Datum ret;
bool isnull;
/* short-circuit (here and in ExecInitQual) for empty restriction list */
if (state == NULL)
return true;
/* verify that expression was compiled using ExecInitQual */
Assert(state->flags & EEO_FLAG_IS_QUAL);
ret = ExecEvalExprSwitchContext(state, econtext, &isnull);
/* EEOP_QUAL should never return NULL */
Assert(!isnull);
return DatumGetBool(ret);
}
#endif
extern bool ExecCheck(ExprState *state, ExprContext *context);
/*
* prototypes from functions in execSRF.c
*/
extern SetExprState *ExecInitTableFunctionResult(Expr *expr,
ExprContext *econtext, PlanState *parent);
extern Tuplestorestate *ExecMakeTableFunctionResult(SetExprState *setexpr,
ExprContext *econtext,
MemoryContext argContext,
TupleDesc expectedDesc,
bool randomAccess);
extern Datum ExecMakeFunctionResultSet(FuncExprState *fcache,
extern SetExprState *ExecInitFunctionResultSet(Expr *expr,
ExprContext *econtext, PlanState *parent);
extern Datum ExecMakeFunctionResultSet(SetExprState *fcache,
ExprContext *econtext,
bool *isNull,
ExprDoneCond *isDone);
extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
bool *isNull);
extern ExprState *ExecInitExpr(Expr *node, PlanState *parent);
extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
extern int ExecTargetListLength(List *targetlist);
extern int ExecCleanTargetListLength(List *targetlist);
extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo);
/*
* prototypes from functions in execScan.c
@ -355,10 +472,6 @@ extern void ExecAssignExprContext(EState *estate, PlanState *planstate);
extern void ExecAssignResultType(PlanState *planstate, TupleDesc tupDesc);
extern void ExecAssignResultTypeFromTL(PlanState *planstate);
extern TupleDesc ExecGetResultType(PlanState *planstate);
extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList,
ExprContext *econtext,
TupleTableSlot *slot,
TupleDesc inputDesc);
extern void ExecAssignProjectionInfo(PlanState *planstate,
TupleDesc inputDesc);
extern void ExecFreeExprContext(PlanState *planstate);
@ -376,8 +489,17 @@ extern void RegisterExprContextCallback(ExprContext *econtext,
extern void UnregisterExprContextCallback(ExprContext *econtext,
ExprContextCallbackFunction function,
Datum arg);
extern void ExecLockNonLeafAppendTables(List *partitioned_rels, EState *estate);
extern Datum GetAttributeByName(HeapTupleHeader tuple, const char *attname,
bool *isNull);
extern Datum GetAttributeByNum(HeapTupleHeader tuple, AttrNumber attrno,
bool *isNull);
extern int ExecTargetListLength(List *targetlist);
extern int ExecCleanTargetListLength(List *targetlist);
/*
* prototypes from functions in execIndexing.c
*/

View File

@ -20,6 +20,10 @@ extern SubPlanState *ExecInitSubPlan(SubPlan *subplan, PlanState *parent);
extern AlternativeSubPlanState *ExecInitAlternativeSubPlan(AlternativeSubPlan *asplan, PlanState *parent);
extern Datum ExecSubPlan(SubPlanState *node, ExprContext *econtext, bool *isNull);
extern Datum ExecAlternativeSubPlan(AlternativeSubPlanState *node, ExprContext *econtext, bool *isNull);
extern void ExecReScanSetParamPlan(SubPlanState *node, PlanState *parent);
extern void ExecSetParamPlan(SubPlanState *node, ExprContext *econtext);

View File

@ -49,6 +49,9 @@ typedef Datum (*PGFunction) (FunctionCallInfo fcinfo);
* arguments, rather than about the function itself. But it's convenient
* to store it here rather than in FunctionCallInfoData, where it might more
* logically belong.
*
* fn_extra is available for use by the called function; all other fields
* should be treated as read-only after the struct is created.
*/
typedef struct FmgrInfo
{
@ -65,6 +68,11 @@ typedef struct FmgrInfo
/*
* This struct is the data actually passed to an fmgr-called function.
*
* The called function is expected to set isnull, and possibly resultinfo or
* fields in whatever resultinfo points to. It should not change any other
* fields. (In particular, scribbling on the argument arrays is a bad idea,
* since some callers assume they can re-call with the same arguments.)
*/
typedef struct FunctionCallInfoData
{

View File

@ -30,6 +30,72 @@
#include "storage/condition_variable.h"
/* ----------------
* ExprState node
*
* ExprState is the top-level node for expression evaluation.
* It contains instructions (in ->steps) to evaluate the expression.
* ----------------
*/
struct ExprState; /* forward references in this file */
struct ExprContext;
struct ExprEvalStep; /* avoid including execExpr.h everywhere */
typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
struct ExprContext *econtext,
bool *isNull);
/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */
/* expression is for use with ExecQual() */
#define EEO_FLAG_IS_QUAL (1 << 0)
typedef struct ExprState
{
Node tag;
uint8 flags; /* bitmask of EEO_FLAG_* bits, see above */
/*
* Storage for result value of a scalar expression, or for individual
* column results within expressions built by ExecBuildProjectionInfo().
*/
bool resnull;
Datum resvalue;
/*
* If projecting a tuple result, this slot holds the result; else NULL.
*/
TupleTableSlot *resultslot;
/*
* Instructions to compute expression's return value.
*/
struct ExprEvalStep *steps;
/*
* Function that actually evaluates the expression. This can be set to
* different values depending on the complexity of the expression.
*/
ExprStateEvalFunc evalfunc;
/* original expression tree, for debugging only */
Expr *expr;
/*
* XXX: following only needed during "compilation", could be thrown away.
*/
int steps_len; /* number of steps currently */
int steps_alloc; /* allocated length of steps array */
Datum *innermost_caseval;
bool *innermost_casenull;
Datum *innermost_domainval;
bool *innermost_domainnull;
} ExprState;
/* ----------------
* IndexInfo information
*
@ -69,7 +135,7 @@ typedef struct IndexInfo
List *ii_Expressions; /* list of Expr */
List *ii_ExpressionsState; /* list of ExprState */
List *ii_Predicate; /* list of Expr */
List *ii_PredicateState; /* list of ExprState */
ExprState *ii_PredicateState;
Oid *ii_ExclusionOps; /* array with one entry per column */
Oid *ii_ExclusionProcs; /* array with one entry per column */
uint16 *ii_ExclusionStrats; /* array with one entry per column */
@ -214,51 +280,21 @@ typedef struct ReturnSetInfo
* that is, form new tuples by evaluation of targetlist expressions.
* Nodes which need to do projections create one of these.
*
* The target tuple slot is kept in ProjectionInfo->pi_state.resultslot.
* ExecProject() evaluates the tlist, forms a tuple, and stores it
* in the given slot. Note that the result will be a "virtual" tuple
* unless ExecMaterializeSlot() is then called to force it to be
* converted to a physical tuple. The slot must have a tupledesc
* that matches the output of the tlist!
*
* The planner very often produces tlists that consist entirely of
* simple Var references (lower levels of a plan tree almost always
* look like that). And top-level tlists are often mostly Vars too.
* We therefore optimize execution of simple-Var tlist entries.
* The pi_targetlist list actually contains only the tlist entries that
* aren't simple Vars, while those that are Vars are processed using the
* varSlotOffsets/varNumbers/varOutputCols arrays.
*
* The lastXXXVar fields are used to optimize fetching of fields from
* input tuples: they let us do a slot_getsomeattrs() call to ensure
* that all needed attributes are extracted in one pass.
*
* targetlist target list for projection (non-Var expressions only)
* exprContext expression context in which to evaluate targetlist
* slot slot to place projection result in
* directMap true if varOutputCols[] is an identity map
* numSimpleVars number of simple Vars found in original tlist
* varSlotOffsets array indicating which slot each simple Var is from
* varNumbers array containing input attr numbers of simple Vars
* varOutputCols array containing output attr numbers of simple Vars
* lastInnerVar highest attnum from inner tuple slot (0 if none)
* lastOuterVar highest attnum from outer tuple slot (0 if none)
* lastScanVar highest attnum from scan tuple slot (0 if none)
* ----------------
*/
typedef struct ProjectionInfo
{
NodeTag type;
List *pi_targetlist;
/* instructions to evaluate projection */
ExprState pi_state;
/* expression context in which to evaluate expression */
ExprContext *pi_exprContext;
TupleTableSlot *pi_slot;
bool pi_directMap;
int pi_numSimpleVars;
int *pi_varSlotOffsets;
int *pi_varNumbers;
int *pi_varOutputCols;
int pi_lastInnerVar;
int pi_lastOuterVar;
int pi_lastScanVar;
} ProjectionInfo;
/* ----------------
@ -340,20 +376,20 @@ typedef struct ResultRelInfo
IndexInfo **ri_IndexRelationInfo;
TriggerDesc *ri_TrigDesc;
FmgrInfo *ri_TrigFunctions;
List **ri_TrigWhenExprs;
ExprState **ri_TrigWhenExprs;
Instrumentation *ri_TrigInstrument;
struct FdwRoutine *ri_FdwRoutine;
void *ri_FdwState;
bool ri_usesFdwDirectModify;
List *ri_WithCheckOptions;
List *ri_WithCheckOptionExprs;
List **ri_ConstraintExprs;
ExprState **ri_ConstraintExprs;
JunkFilter *ri_junkFilter;
ProjectionInfo *ri_projectReturning;
ProjectionInfo *ri_onConflictSetProj;
List *ri_onConflictSetWhere;
ExprState *ri_onConflictSetWhere;
List *ri_PartitionCheck;
List *ri_PartitionCheckExpr;
ExprState *ri_PartitionCheckExpr;
Relation ri_PartitionRoot;
} ResultRelInfo;
@ -564,139 +600,63 @@ typedef tuplehash_iterator TupleHashIterator;
/* ----------------------------------------------------------------
* Expression State Trees
* Expression State Nodes
*
* Each executable expression tree has a parallel ExprState tree.
*
* Unlike PlanState, there is not an exact one-for-one correspondence between
* ExprState node types and Expr node types. Many Expr node types have no
* need for node-type-specific run-time state, and so they can use plain
* ExprState or GenericExprState as their associated ExprState node type.
* Formerly, there was a separate executor expression state node corresponding
* to each node in a planned expression tree. That's no longer the case; for
* common expression node types, all the execution info is embedded into
* step(s) in a single ExprState node. But we still have a few executor state
* node types for selected expression node types, mostly those in which info
* has to be shared with other parts of the execution state tree.
* ----------------------------------------------------------------
*/
/* ----------------
* ExprState node
*
* ExprState is the common superclass for all ExprState-type nodes.
*
* It can also be instantiated directly for leaf Expr nodes that need no
* local run-time state (such as Var, Const, or Param).
*
* To save on dispatch overhead, each ExprState node contains a function
* pointer to the routine to execute to evaluate the node.
* ----------------
*/
typedef struct ExprState ExprState;
typedef Datum (*ExprStateEvalFunc) (ExprState *expression,
ExprContext *econtext,
bool *isNull);
struct ExprState
{
NodeTag type;
Expr *expr; /* associated Expr node */
ExprStateEvalFunc evalfunc; /* routine to run to execute node */
};
/* ----------------
* GenericExprState node
*
* This is used for Expr node types that need no local run-time state,
* but have one child Expr node.
* ----------------
*/
typedef struct GenericExprState
{
ExprState xprstate;
ExprState *arg; /* state of my child node */
} GenericExprState;
/* ----------------
* WholeRowVarExprState node
* ----------------
*/
typedef struct WholeRowVarExprState
{
ExprState xprstate;
struct PlanState *parent; /* parent PlanState, or NULL if none */
TupleDesc wrv_tupdesc; /* descriptor for resulting tuples */
JunkFilter *wrv_junkFilter; /* JunkFilter to remove resjunk cols */
} WholeRowVarExprState;
/* ----------------
* AggrefExprState node
* ----------------
*/
typedef struct AggrefExprState
{
ExprState xprstate;
NodeTag type;
Aggref *aggref; /* expression plan node */
int aggno; /* ID number for agg within its plan node */
} AggrefExprState;
/* ----------------
* GroupingFuncExprState node
*
* The list of column numbers refers to the input tuples of the Agg node to
* which the GroupingFunc belongs, and may contain 0 for references to columns
* that are only present in grouping sets processed by different Agg nodes (and
* which are therefore always considered "grouping" here).
* ----------------
*/
typedef struct GroupingFuncExprState
{
ExprState xprstate;
struct AggState *aggstate;
List *clauses; /* integer list of column numbers */
} GroupingFuncExprState;
/* ----------------
* WindowFuncExprState node
* ----------------
*/
typedef struct WindowFuncExprState
{
ExprState xprstate;
List *args; /* states of argument expressions */
NodeTag type;
WindowFunc *wfunc; /* expression plan node */
List *args; /* ExprStates for argument expressions */
ExprState *aggfilter; /* FILTER expression */
int wfuncno; /* ID number for wfunc within its plan node */
} WindowFuncExprState;
/* ----------------
* ArrayRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
typedef struct ArrayRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
int16 refattrlength; /* typlen of array type */
int16 refelemlength; /* typlen of the array element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
} ArrayRefExprState;
/* ----------------
* FuncExprState node
* SetExprState node
*
* Although named for FuncExpr, this is also used for OpExpr, DistinctExpr,
* and NullIf nodes; be careful to check what xprstate.expr is actually
* pointing at!
* State for evaluating a potentially set-returning expression (like FuncExpr
* or OpExpr). In some cases, like some of the expressions in ROWS FROM(...)
* the expression might not be a SRF, but nonetheless it uses the same
* machinery as SRFs; it will be treated as a SRF returning a single row.
* ----------------
*/
typedef struct FuncExprState
typedef struct SetExprState
{
ExprState xprstate;
List *args; /* states of argument expressions */
NodeTag type;
Expr *expr; /* expression plan node */
List *args; /* ExprStates for argument expressions */
/*
* In ROWS FROM, functions can be inlined, removing the FuncExpr normally
* inside. In such a case this is the compiled expression (which cannot
* return a set), which'll be evaluated using regular ExecEvalExpr().
*/
ExprState *elidedFuncState;
/*
* Function manager's lookup info for the target function. If func.fn_oid
@ -738,7 +698,7 @@ typedef struct FuncExprState
/*
* Flag to remember whether we have registered a shutdown callback for
* this FuncExprState. We do so only if funcResultStore or setArgsValid
* this SetExprState. We do so only if funcResultStore or setArgsValid
* has been set at least once (since all the callback is for is to release
* the tuplestore or clear setArgsValid).
*/
@ -750,33 +710,7 @@ typedef struct FuncExprState
* argument values between calls, when setArgsValid is true.
*/
FunctionCallInfoData fcinfo_data;
} FuncExprState;
/* ----------------
* ScalarArrayOpExprState node
*
* This is a FuncExprState plus some additional data.
* ----------------
*/
typedef struct ScalarArrayOpExprState
{
FuncExprState fxprstate;
/* Cached info about array element type */
Oid element_type;
int16 typlen;
bool typbyval;
char typalign;
} ScalarArrayOpExprState;
/* ----------------
* BoolExprState node
* ----------------
*/
typedef struct BoolExprState
{
ExprState xprstate;
List *args; /* states of argument expression(s) */
} BoolExprState;
} SetExprState;
/* ----------------
* SubPlanState node
@ -784,7 +718,8 @@ typedef struct BoolExprState
*/
typedef struct SubPlanState
{
ExprState xprstate;
NodeTag type;
SubPlan *subplan; /* expression plan node */
struct PlanState *planstate; /* subselect plan's state tree */
struct PlanState *parent; /* parent plan node's state tree */
ExprState *testexpr; /* state of combining expression */
@ -814,203 +749,18 @@ typedef struct SubPlanState
*/
typedef struct AlternativeSubPlanState
{
ExprState xprstate;
List *subplans; /* states of alternative subplans */
NodeTag type;
AlternativeSubPlan *subplan; /* expression plan node */
List *subplans; /* SubPlanStates of alternative subplans */
int active; /* list index of the one we're using */
} AlternativeSubPlanState;
/* ----------------
* FieldSelectState node
* ----------------
*/
typedef struct FieldSelectState
{
ExprState xprstate;
ExprState *arg; /* input expression */
TupleDesc argdesc; /* tupdesc for most recent input */
} FieldSelectState;
/* ----------------
* FieldStoreState node
* ----------------
*/
typedef struct FieldStoreState
{
ExprState xprstate;
ExprState *arg; /* input tuple value */
List *newvals; /* new value(s) for field(s) */
TupleDesc argdesc; /* tupdesc for most recent input */
} FieldStoreState;
/* ----------------
* CoerceViaIOState node
* ----------------
*/
typedef struct CoerceViaIOState
{
ExprState xprstate;
ExprState *arg; /* input expression */
FmgrInfo outfunc; /* lookup info for source output function */
FmgrInfo infunc; /* lookup info for result input function */
Oid intypioparam; /* argument needed for input function */
} CoerceViaIOState;
/* ----------------
* ArrayCoerceExprState node
* ----------------
*/
typedef struct ArrayCoerceExprState
{
ExprState xprstate;
ExprState *arg; /* input array value */
Oid resultelemtype; /* element type of result array */
FmgrInfo elemfunc; /* lookup info for element coercion function */
/* use struct pointer to avoid including array.h here */
struct ArrayMapState *amstate; /* workspace for array_map */
} ArrayCoerceExprState;
/* ----------------
* ConvertRowtypeExprState node
* ----------------
*/
typedef struct ConvertRowtypeExprState
{
ExprState xprstate;
ExprState *arg; /* input tuple value */
TupleDesc indesc; /* tupdesc for source rowtype */
TupleDesc outdesc; /* tupdesc for result rowtype */
/* use "struct" so we needn't include tupconvert.h here */
struct TupleConversionMap *map;
bool initialized;
} ConvertRowtypeExprState;
/* ----------------
* CaseExprState node
* ----------------
*/
typedef struct CaseExprState
{
ExprState xprstate;
ExprState *arg; /* implicit equality comparison argument */
List *args; /* the arguments (list of WHEN clauses) */
ExprState *defresult; /* the default result (ELSE clause) */
int16 argtyplen; /* if arg is provided, its typlen */
} CaseExprState;
/* ----------------
* CaseWhenState node
* ----------------
*/
typedef struct CaseWhenState
{
ExprState xprstate;
ExprState *expr; /* condition expression */
ExprState *result; /* substitution result */
} CaseWhenState;
/* ----------------
* ArrayExprState node
*
* Note: ARRAY[] expressions always produce varlena arrays, never fixed-length
* arrays.
* ----------------
*/
typedef struct ArrayExprState
{
ExprState xprstate;
List *elements; /* states for child nodes */
int16 elemlength; /* typlen of the array element type */
bool elembyval; /* is the element type pass-by-value? */
char elemalign; /* typalign of the element type */
} ArrayExprState;
/* ----------------
* RowExprState node
* ----------------
*/
typedef struct RowExprState
{
ExprState xprstate;
List *args; /* the arguments */
TupleDesc tupdesc; /* descriptor for result tuples */
} RowExprState;
/* ----------------
* RowCompareExprState node
* ----------------
*/
typedef struct RowCompareExprState
{
ExprState xprstate;
List *largs; /* the left-hand input arguments */
List *rargs; /* the right-hand input arguments */
FmgrInfo *funcs; /* array of comparison function info */
Oid *collations; /* array of collations to use */
} RowCompareExprState;
/* ----------------
* CoalesceExprState node
* ----------------
*/
typedef struct CoalesceExprState
{
ExprState xprstate;
List *args; /* the arguments */
} CoalesceExprState;
/* ----------------
* MinMaxExprState node
* ----------------
*/
typedef struct MinMaxExprState
{
ExprState xprstate;
List *args; /* the arguments */
FmgrInfo cfunc; /* lookup info for comparison func */
} MinMaxExprState;
/* ----------------
* XmlExprState node
* ----------------
*/
typedef struct XmlExprState
{
ExprState xprstate;
List *named_args; /* ExprStates for named arguments */
List *args; /* ExprStates for other arguments */
} XmlExprState;
/* ----------------
* NullTestState node
* ----------------
*/
typedef struct NullTestState
{
ExprState xprstate;
ExprState *arg; /* input expression */
/* used only if input is of composite type: */
TupleDesc argdesc; /* tupdesc for most recent input */
} NullTestState;
/* ----------------
* CoerceToDomainState node
* ----------------
*/
typedef struct CoerceToDomainState
{
ExprState xprstate;
ExprState *arg; /* input expression */
/* Cached set of constraints that need to be checked */
/* use struct pointer to avoid including typcache.h here */
struct DomainConstraintRef *constraint_ref;
} CoerceToDomainState;
/*
* DomainConstraintState - one item to check during CoerceToDomain
*
* Note: this is just a Node, and not an ExprState, because it has no
* corresponding Expr to link to. Nonetheless it is part of an ExprState
* tree, so we give it a name following the xxxState convention.
* Note: we consider this to be part of an ExprState tree, so we give it
* a name following the xxxState convention. But there's no directly
* associated plan-tree node.
*/
typedef enum DomainConstraintType
{
@ -1023,7 +773,8 @@ typedef struct DomainConstraintState
NodeTag type;
DomainConstraintType constrainttype; /* constraint type */
char *name; /* name of constraint (for error msgs) */
ExprState *check_expr; /* for CHECK, a boolean expression */
Expr *check_expr; /* for CHECK, a boolean expression */
ExprState *check_exprstate; /* check_expr's eval state, or NULL */
} DomainConstraintState;
@ -1060,8 +811,7 @@ typedef struct PlanState
* state trees parallel links in the associated plan tree (except for the
* subPlan list, which does not exist in the plan tree).
*/
List *targetlist; /* target list to be computed at this node */
List *qual; /* implicitly-ANDed qual conditions */
ExprState *qual; /* boolean qual condition */
struct PlanState *lefttree; /* input plan tree(s) */
struct PlanState *righttree;
List *initPlan; /* Init SubPlanState nodes (un-correlated expr
@ -1133,11 +883,15 @@ typedef struct ResultState
/* ----------------
* ProjectSetState information
*
* Note: at least one of the "elems" will be a SetExprState; the rest are
* regular ExprStates.
* ----------------
*/
typedef struct ProjectSetState
{
PlanState ps; /* its first field is NodeTag */
Node **elems; /* array of expression states */
ExprDoneCond *elemdone; /* array of per-SRF is-done states */
int nelems; /* length of elemdone[] array */
bool pending_srf_tuples; /* still evaluating srfs in tlist? */
@ -1372,7 +1126,7 @@ typedef struct
typedef struct IndexScanState
{
ScanState ss; /* its first field is NodeTag */
List *indexqualorig;
ExprState *indexqualorig;
List *indexorderbyorig;
ScanKey iss_ScanKeys;
int iss_NumScanKeys;
@ -1418,7 +1172,7 @@ typedef struct IndexScanState
typedef struct IndexOnlyScanState
{
ScanState ss; /* its first field is NodeTag */
List *indexqual;
ExprState *indexqual;
ScanKey ioss_ScanKeys;
int ioss_NumScanKeys;
ScanKey ioss_OrderByKeys;
@ -1534,7 +1288,7 @@ typedef struct ParallelBitmapHeapState
typedef struct BitmapHeapScanState
{
ScanState ss; /* its first field is NodeTag */
List *bitmapqualorig;
ExprState *bitmapqualorig;
TIDBitmap *tbm;
TBMIterator *tbmiterator;
TBMIterateResult *tbmres;
@ -1554,16 +1308,18 @@ typedef struct BitmapHeapScanState
/* ----------------
* TidScanState information
*
* tidexprs list of TidExpr structs (see nodeTidscan.c)
* isCurrentOf scan has a CurrentOfExpr qual
* NumTids number of tids in this scan
* TidPtr index of currently fetched tid
* TidList evaluated item pointers (array of size NumTids)
* htup currently-fetched tuple, if any
* ----------------
*/
typedef struct TidScanState
{
ScanState ss; /* its first field is NodeTag */
List *tss_tidquals; /* list of ExprState nodes */
List *tss_tidexprs;
bool tss_isCurrentOf;
int tss_NumTids;
int tss_TidPtr;
@ -1712,7 +1468,7 @@ typedef struct WorkTableScanState
typedef struct ForeignScanState
{
ScanState ss; /* its first field is NodeTag */
List *fdw_recheck_quals; /* original quals not in ss.ps.qual */
ExprState *fdw_recheck_quals; /* original quals not in ss.ps.qual */
Size pscan_len; /* size of parallel coordination information */
/* use struct pointer to avoid including fdwapi.h here */
struct FdwRoutine *fdwroutine;
@ -1759,7 +1515,7 @@ typedef struct JoinState
{
PlanState ps;
JoinType jointype;
List *joinqual; /* JOIN quals (in addition to ps.qual) */
ExprState *joinqual; /* JOIN quals (in addition to ps.qual) */
} JoinState;
/* ----------------
@ -1857,7 +1613,7 @@ typedef struct HashJoinTableData *HashJoinTable;
typedef struct HashJoinState
{
JoinState js; /* its first field is NodeTag */
List *hashclauses; /* list of ExprState nodes */
ExprState *hashclauses;
List *hj_OuterHashKeys; /* list of ExprState nodes */
List *hj_InnerHashKeys; /* list of ExprState nodes */
List *hj_HashOperators; /* list of operator OIDs */

View File

@ -192,36 +192,18 @@ typedef enum NodeTag
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
*
* These correspond (not always one-for-one) to primitive nodes derived
* from Expr.
* ExprState represents the evaluation state for a whole expression tree.
* Most Expr-based plan nodes do not have a corresponding expression state
* node, they're fully handled within execExpr* - but sometimes the state
* needs to be shared with other parts of the executor, as for example
* with AggrefExprState, which nodeAgg.c has to modify.
*/
T_ExprState,
T_GenericExprState,
T_WholeRowVarExprState,
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
T_ArrayRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
T_SetExprState,
T_SubPlanState,
T_AlternativeSubPlanState,
T_FieldSelectState,
T_FieldStoreState,
T_CoerceViaIOState,
T_ArrayCoerceExprState,
T_ConvertRowtypeExprState,
T_CaseExprState,
T_CaseWhenState,
T_ArrayExprState,
T_RowExprState,
T_RowCompareExprState,
T_CoalesceExprState,
T_MinMaxExprState,
T_XmlExprState,
T_NullTestState,
T_CoerceToDomainState,
T_DomainConstraintState,
/*

View File

@ -132,6 +132,7 @@ typedef struct DomainConstraintRef
List *constraints; /* list of DomainConstraintState nodes */
MemoryContext refctx; /* context holding DomainConstraintRef */
TypeCacheEntry *tcache; /* typcache entry for domain type */
bool need_exprstate; /* does caller need check_exprstate? */
/* Management data --- treat these fields as private to typcache.c */
DomainConstraintCache *dcc; /* current constraints, or NULL if none */
@ -142,7 +143,7 @@ typedef struct DomainConstraintRef
extern TypeCacheEntry *lookup_type_cache(Oid type_id, int flags);
extern void InitDomainConstraintRef(Oid type_id, DomainConstraintRef *ref,
MemoryContext refctx);
MemoryContext refctx, bool need_exprstate);
extern void UpdateDomainConstraintRef(DomainConstraintRef *ref);

View File

@ -61,7 +61,9 @@ extern void xml_ereport(PgXmlErrorContext *errcxt, int level, int sqlcode,
const char *msg);
extern xmltype *xmlconcat(List *args);
extern xmltype *xmlelement(XmlExprState *xmlExpr, ExprContext *econtext);
extern xmltype *xmlelement(XmlExpr *xexpr,
Datum *named_argvalue, bool *named_argnull,
Datum *argvalue, bool *argnull);
extern xmltype *xmlparse(text *data, XmlOptionType xmloption, bool preserve_whitespace);
extern xmltype *xmlpi(char *target, text *arg, bool arg_is_null, bool *result_is_null);
extern xmltype *xmlroot(xmltype *data, text *version, int standalone);

View File

@ -145,7 +145,7 @@ typedef struct /* cast_hash table entry */
{
plpgsql_CastHashKey key; /* hash key --- MUST BE FIRST */
Expr *cast_expr; /* cast expression, or NULL if no-op cast */
/* The ExprState tree is valid only when cast_lxid matches current LXID */
/* ExprState is valid only when cast_lxid matches current LXID */
ExprState *cast_exprstate; /* expression's eval tree */
bool cast_in_use; /* true while we're executing eval tree */
LocalTransactionId cast_lxid;
@ -4710,7 +4710,8 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
* Like ExecEvalArrayRef(), complain if any subscript is null.
* Like the expression built by ExecInitArrayRef(), complain
* if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{

View File

@ -308,7 +308,7 @@ SELECT * FROM CASE_TBL;
-- Nested CASE expressions
--
-- This test exercises a bug caused by aliasing econtext->caseValue_isNull
-- with the isNull argument of the inner CASE's ExecEvalCase() call. After
-- with the isNull argument of the inner CASE's CaseExpr evaluation. After
-- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
-- the isNull flag for the case test value incorrectly became true, causing
-- the third WHEN-clause not to match. The volatile function calls are needed

View File

@ -586,14 +586,8 @@ ERROR: must be owner of function testfunc1
DROP FUNCTION testfunc1(int); -- ok
-- restore to sanity
GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
-- verify privilege checks on coercions
-- verify privilege checks on array-element coercions
BEGIN;
SELECT NULL::int4[]::int8[];
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[];
int8
------
@ -601,12 +595,6 @@ SELECT '{1}'::int4[]::int8[];
(1 row)
REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
SELECT NULL::int4[]::int8[];
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[]; --superuser, suceed
int8
------
@ -614,12 +602,6 @@ SELECT '{1}'::int4[]::int8[]; --superuser, suceed
(1 row)
SET SESSION AUTHORIZATION regress_user4;
SELECT NULL::int4[]::int8[]; --other user, no elements to convert
int8
------
(1 row)
SELECT '{1}'::int4[]::int8[]; --other user, fail
ERROR: permission denied for function int8
ROLLBACK;

View File

@ -166,7 +166,7 @@ SELECT * FROM CASE_TBL;
--
-- This test exercises a bug caused by aliasing econtext->caseValue_isNull
-- with the isNull argument of the inner CASE's ExecEvalCase() call. After
-- with the isNull argument of the inner CASE's CaseExpr evaluation. After
-- evaluating the vol(null) expression in the inner CASE's second WHEN-clause,
-- the isNull flag for the case test value incorrectly became true, causing
-- the third WHEN-clause not to match. The volatile function calls are needed

View File

@ -398,15 +398,12 @@ DROP FUNCTION testfunc1(int); -- ok
-- restore to sanity
GRANT ALL PRIVILEGES ON LANGUAGE sql TO PUBLIC;
-- verify privilege checks on coercions
-- verify privilege checks on array-element coercions
BEGIN;
SELECT NULL::int4[]::int8[];
SELECT '{1}'::int4[]::int8[];
REVOKE ALL ON FUNCTION int8(integer) FROM PUBLIC;
SELECT NULL::int4[]::int8[];
SELECT '{1}'::int4[]::int8[]; --superuser, suceed
SET SESSION AUTHORIZATION regress_user4;
SELECT NULL::int4[]::int8[]; --other user, no elements to convert
SELECT '{1}'::int4[]::int8[]; --other user, fail
ROLLBACK;

View File

@ -120,7 +120,7 @@ ArrayMapState
ArrayMetaState
ArrayParseState
ArrayRef
ArrayRefExprState
ArrayRefState
ArrayRemapInfo
ArrayType
AsyncQueueControl
@ -578,6 +578,8 @@ ExprContext_CB
ExprDoneCond
ExprState
ExprStateEvalFunc
ExprEvalOp
ExprEvalStep
ExtensibleNode
ExtensibleNodeEntry
ExtensibleNodeMethods
@ -1073,6 +1075,7 @@ LWLockPadded
LWLockTranche
LabelProvider
LargeObjectDesc
LastAttnumInfo
Latch
LerpFunc
LexDescr
@ -1908,6 +1911,7 @@ Session
SetConstraintState
SetConstraintStateData
SetConstraintTriggerData
SetExprState
SetFunctionReturnMode
SetOp
SetOpCmd