1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-30 11:03:19 +03:00

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

@ -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