agora inbox for [email protected]help / color / mirror / Atom feed
[PATCH 03/20] WIP: Faster expression processing and targetlist projection. 21+ messages / 2 participants [nested] [flat]
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH 03/20] WIP: Faster expression processing and targetlist projection. @ 2016-07-13 04:06 Andres Freund <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Andres Freund @ 2016-07-13 04:06 UTC (permalink / raw) --- src/backend/bootstrap/bootstrap.c | 2 +- src/backend/catalog/index.c | 38 +- src/backend/catalog/toasting.c | 2 +- src/backend/commands/analyze.c | 10 +- src/backend/commands/indexcmds.c | 4 +- src/backend/commands/tablecmds.c | 14 +- src/backend/commands/trigger.c | 8 +- src/backend/executor/Makefile | 4 +- src/backend/executor/execExpr.c | 915 ++++++++++++++++++++++++++++++ src/backend/executor/execIndexing.c | 21 +- src/backend/executor/execMain.c | 17 +- src/backend/executor/execQual.c | 414 +------------- src/backend/executor/execScan.c | 4 +- src/backend/executor/execUtils.c | 140 +---- src/backend/executor/nodeAgg.c | 14 +- src/backend/executor/nodeBitmapHeapscan.c | 18 +- src/backend/executor/nodeCtescan.c | 7 +- src/backend/executor/nodeCustom.c | 7 +- src/backend/executor/nodeForeignscan.c | 16 +- src/backend/executor/nodeFunctionscan.c | 7 +- src/backend/executor/nodeGather.c | 7 +- src/backend/executor/nodeGroup.c | 11 +- src/backend/executor/nodeHash.c | 11 +- src/backend/executor/nodeHashjoin.c | 47 +- src/backend/executor/nodeIndexonlyscan.c | 16 +- src/backend/executor/nodeIndexscan.c | 20 +- src/backend/executor/nodeMergejoin.c | 32 +- src/backend/executor/nodeModifyTable.c | 36 +- src/backend/executor/nodeNestloop.c | 29 +- src/backend/executor/nodeResult.c | 17 +- src/backend/executor/nodeSamplescan.c | 7 +- src/backend/executor/nodeSeqscan.c | 7 +- src/backend/executor/nodeSubplan.c | 12 +- src/backend/executor/nodeSubqueryscan.c | 7 +- src/backend/executor/nodeTidscan.c | 7 +- src/backend/executor/nodeValuesscan.c | 7 +- src/backend/executor/nodeWindowAgg.c | 2 +- src/backend/executor/nodeWorktablescan.c | 7 +- src/bin/initdb/initdb.c | 4 +- src/include/executor/executor.h | 37 +- src/include/nodes/execnodes.h | 177 ++++-- src/include/nodes/nodes.h | 2 + 42 files changed, 1353 insertions(+), 811 deletions(-) create mode 100644 src/backend/executor/execExpr.c diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c index e518e17..5b805b5 100644 --- a/src/backend/bootstrap/bootstrap.c +++ b/src/backend/bootstrap/bootstrap.c @@ -1088,7 +1088,7 @@ index_register(Oid heap, /* predicate will likely be null, but may as well copy it */ newind->il_info->ii_Predicate = (List *) copyObject(indexInfo->ii_Predicate); - newind->il_info->ii_PredicateState = NIL; + newind->il_info->ii_PredicateState = NULL; /* no exclusion constraints at bootstrap time, so no need to copy */ Assert(indexInfo->ii_ExclusionOps == NULL); Assert(indexInfo->ii_ExclusionProcs == NULL); diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7b30e46..053a116 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -1645,7 +1645,7 @@ BuildIndexInfo(Relation index) /* fetch index predicate if any */ ii->ii_Predicate = RelationGetIndexPredicate(index); - ii->ii_PredicateState = NIL; + ii->ii_PredicateState = NULL; /* fetch exclusion constraint info if any */ if (indexStruct->indisexclusion) @@ -2193,7 +2193,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; double reltuples; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2232,9 +2232,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. In a normal index build, we use @@ -2537,9 +2535,9 @@ IndexBuildHeapRangeScan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2604,7 +2602,7 @@ IndexBuildHeapRangeScan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; return reltuples; } @@ -2631,7 +2629,7 @@ IndexCheckExclusion(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2657,9 +2655,7 @@ IndexCheckExclusion(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Scan all live tuples in the base relation. @@ -2684,9 +2680,9 @@ IndexCheckExclusion(Relation heapRelation, /* * In a partial index, ignore tuples that don't satisfy the predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -2717,7 +2713,7 @@ IndexCheckExclusion(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } @@ -2947,7 +2943,7 @@ validate_index_heapscan(Relation heapRelation, HeapTuple heapTuple; Datum values[INDEX_MAX_KEYS]; bool isnull[INDEX_MAX_KEYS]; - List *predicate; + ExprState2 *predicate; TupleTableSlot *slot; EState *estate; ExprContext *econtext; @@ -2977,9 +2973,7 @@ validate_index_heapscan(Relation heapRelation, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate, if any. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* * Prepare for scan of the base relation. We need just those tuples @@ -3106,9 +3100,9 @@ validate_index_heapscan(Relation heapRelation, * In a partial index, discard tuples that don't satisfy the * predicate. */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } @@ -3161,7 +3155,7 @@ validate_index_heapscan(Relation heapRelation, /* These may have been pointing to the now-gone estate */ indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; } diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c index 564e10e..c618dc1 100644 --- a/src/backend/catalog/toasting.c +++ b/src/backend/catalog/toasting.c @@ -307,7 +307,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c index 5fcedd7..00c9d30 100644 --- a/src/backend/commands/analyze.c +++ b/src/backend/commands/analyze.c @@ -701,7 +701,7 @@ compute_index_stats(Relation onerel, double totalrows, TupleTableSlot *slot; EState *estate; ExprContext *econtext; - List *predicate; + ExprState2 *predicate; Datum *exprvals; bool *exprnulls; int numindexrows, @@ -727,9 +727,7 @@ compute_index_stats(Relation onerel, double totalrows, econtext->ecxt_scantuple = slot; /* Set up execution state for predicate. */ - predicate = (List *) - ExecPrepareExpr((Expr *) indexInfo->ii_Predicate, - estate); + predicate = ExecPrepareQual(indexInfo->ii_Predicate, estate); /* Compute and save index expression values */ exprvals = (Datum *) palloc(numrows * attr_cnt * sizeof(Datum)); @@ -752,9 +750,9 @@ compute_index_stats(Relation onerel, double totalrows, ExecStoreTuple(heapTuple, slot, InvalidBuffer, false); /* If index is partial, check predicate */ - if (predicate != NIL) + if (predicate != NULL) { - if (!ExecQual(predicate, econtext, false)) + if (!ExecQual(predicate)) continue; } numindexrows++; diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index d14d540..508130e 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -180,7 +180,7 @@ CheckIndexCompatible(Oid oldId, indexInfo = makeNode(IndexInfo); indexInfo->ii_Expressions = NIL; indexInfo->ii_ExpressionsState = NIL; - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; @@ -549,7 +549,7 @@ DefineIndex(Oid relationId, indexInfo->ii_Expressions = NIL; /* for now */ indexInfo->ii_ExpressionsState = NIL; indexInfo->ii_Predicate = make_ands_implicit((Expr *) stmt->whereClause); - indexInfo->ii_PredicateState = NIL; + indexInfo->ii_PredicateState = NULL; indexInfo->ii_ExclusionOps = NULL; indexInfo->ii_ExclusionProcs = NULL; indexInfo->ii_ExclusionStrats = NULL; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 86e9814..804ca92 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -179,7 +179,7 @@ typedef struct NewConstraint Oid refindid; /* OID of PK's index, if FOREIGN */ Oid conid; /* OID of pg_constraint entry, if FOREIGN */ Node *qual; /* Check expr or CONSTR_FOREIGN Constraint */ - List *qualstate; /* Execution state for CHECK */ + ExprState2 *qualstate; /* Execution state for CHECK */ } NewConstraint; /* @@ -4010,8 +4010,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) { case CONSTR_CHECK: needscan = true; - con->qualstate = (List *) - ExecPrepareExpr((Expr *) con->qual, estate); + con->qualstate = ExecPrepareQual((List *) con->qual, estate); break; case CONSTR_FOREIGN: /* Nothing to do here */ @@ -4195,7 +4194,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) switch (con->contype) { case CONSTR_CHECK: - if (!ExecQual(con->qualstate, econtext, true)) + if (!ExecQual(con->qualstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", @@ -7308,7 +7307,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) Datum val; char *conbin; Expr *origexpr; - List *exprstate; + ExprState2 *exprstate; TupleDesc tupdesc; HeapScanDesc scan; HeapTuple tuple; @@ -7339,8 +7338,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) HeapTupleGetOid(constrtup)); conbin = TextDatumGetCString(val); origexpr = (Expr *) stringToNode(conbin); - exprstate = (List *) - ExecPrepareExpr((Expr *) make_ands_implicit(origexpr), estate); + exprstate = ExecPrepareQual(make_ands_implicit(origexpr), estate); econtext = GetPerTupleExprContext(estate); tupdesc = RelationGetDescr(rel); @@ -7360,7 +7358,7 @@ validateCheckConstraint(Relation rel, HeapTuple constrtup) { ExecStoreTuple(tuple, slot, InvalidBuffer, false); - if (!ExecQual(exprstate, econtext, true)) + if (!ExecQual(exprstate)) ereport(ERROR, (errcode(ERRCODE_CHECK_VIOLATION), errmsg("check constraint \"%s\" is violated by some row", diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 99a659a..0929652 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -2867,7 +2867,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, if (trigger->tgqual) { TupleDesc tupdesc = RelationGetDescr(relinfo->ri_RelationDesc); - List **predicate; + ExprState2 **predicate; ExprContext *econtext; TupleTableSlot *oldslot = NULL; TupleTableSlot *newslot = NULL; @@ -2888,7 +2888,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, * nodetrees for it. Keep them in the per-query memory context so * they'll survive throughout the query. */ - if (*predicate == NIL) + if (*predicate == NULL) { Node *tgqual; @@ -2899,7 +2899,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, ChangeVarNodes(tgqual, PRS2_NEW_VARNO, OUTER_VAR, 0); /* ExecQual wants implicit-AND form */ tgqual = (Node *) make_ands_implicit((Expr *) tgqual); - *predicate = (List *) ExecPrepareExpr((Expr *) tgqual, estate); + *predicate = ExecPrepareQual((List *) tgqual, estate); MemoryContextSwitchTo(oldContext); } @@ -2947,7 +2947,7 @@ TriggerEnabled(EState *estate, ResultRelInfo *relinfo, */ econtext->ecxt_innertuple = oldslot; econtext->ecxt_outertuple = newslot; - if (!ExecQual(*predicate, econtext, false)) + if (!ExecQual(*predicate)) return false; } diff --git a/src/backend/executor/Makefile b/src/backend/executor/Makefile index 51edd4c..ba7eb50 100644 --- a/src/backend/executor/Makefile +++ b/src/backend/executor/Makefile @@ -12,8 +12,8 @@ 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 \ +OBJS = execAmi.o execCurrent.o execExpr.o execGrouping.o execIndexing.o \ + execJunk.o execMain.o execParallel.o execProcnode.o execQual.o \ execScan.o execTuples.o \ execUtils.o functions.o instrument.o nodeAppend.o nodeAgg.o \ nodeBitmapAnd.o nodeBitmapOr.o \ diff --git a/src/backend/executor/execExpr.c b/src/backend/executor/execExpr.c new file mode 100644 index 0000000..fe5575f --- /dev/null +++ b/src/backend/executor/execExpr.c @@ -0,0 +1,915 @@ +#include "postgres.h" + +#include "access/htup_details.h" +#include "access/nbtree.h" +#include "access/tupconvert.h" +#include "catalog/objectaccess.h" +#include "catalog/pg_type.h" +#include "executor/execdebug.h" +#include "executor/nodeSubplan.h" +#include "funcapi.h" +#include "miscadmin.h" +#include "nodes/makefuncs.h" +#include "nodes/nodeFuncs.h" +#include "optimizer/planner.h" +#include "parser/parse_coerce.h" +#include "parser/parsetree.h" +#include "pgstat.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/memutils.h" +#include "utils/typcache.h" +#include "utils/xml.h" + +static bool ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *context, ExprState2 *state, Datum *resv, bool *resnull); + +static void +ExprEvalPushStep(ExprState2 *es, ExprEvalStep *s) +{ + if (es->steps_alloc == 0) + { + es->steps_alloc = 16; + es->steps = palloc(sizeof(ExprEvalStep) * es->steps_alloc); + } + else if (es->steps_alloc == es->steps_len) + { + es->steps_alloc *= 2; + es->steps = repalloc(es->steps, + sizeof(ExprEvalStep) * es->steps_alloc); + } + + memcpy(&es->steps[es->steps_len++], s, sizeof(ExprEvalStep)); +} + +static TupleTableSlot** varslot(Var *variable, ExprContext *econtext) +{ + TupleTableSlot **slot; + + switch (variable->varno) + { + case INNER_VAR: /* get the tuple from the inner node */ + slot = &econtext->ecxt_innertuple; + break; + + case OUTER_VAR: /* get the tuple from the outer node */ + slot = &econtext->ecxt_outertuple; + break; + + /* INDEX_VAR is handled by default case */ + + default: /* get the tuple from the relation being + * scanned */ + slot = &econtext->ecxt_scantuple; + break; + } + + return slot; +} + +static void +ExecInitOld(ExprState2 *state, PlanState *parent, Expr *expr, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + ExprState *es = ExecInitExpr(expr, parent); + scratch.resvalue = resv; + scratch.resnull = resnull; + scratch.opcode = EEO_EVAL1; + scratch.d.eval1.expr = es; + ExprEvalPushStep(state, &scratch); +} + +static bool +ExecInitFunc2(ExprEvalStep *scratch, Expr *node, List *args, Oid funcid, Oid inputcollid, + PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ListCell *lc; + AclResult aclresult; + int nargs = list_length(args); + FunctionCallInfo fcinfo; + int argno; + + /* Check permission to call function */ + aclresult = pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(funcid)); + InvokeFunctionExecuteHook(funcid); + + /* + * Safety check on nargs. Under normal circumstances this should never + * fail, as parser should check sooner. But possibly it might fail if + * server has been compiled with FUNC_MAX_ARGS smaller than some functions + * declared in pg_proc? + */ + if (nargs > FUNC_MAX_ARGS) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ARGUMENTS), + errmsg_plural("cannot pass more than %d argument to a function", + "cannot pass more than %d arguments to a function", + FUNC_MAX_ARGS, + FUNC_MAX_ARGS))); + + /* Set up the primary fmgr lookup information */ + scratch->d.func.finfo = palloc0(sizeof(*scratch->d.func.finfo)); + scratch->d.func.fcinfo_data = palloc0(sizeof(*scratch->d.func.fcinfo_data)); + + fcinfo = scratch->d.func.fcinfo_data; + fmgr_info_cxt(funcid, scratch->d.func.finfo, econtext->ecxt_per_query_memory); + fmgr_info_set_expr((Node *) node, scratch->d.func.finfo); + InitFunctionCallInfoData(*fcinfo, scratch->d.func.finfo, + nargs, inputcollid, NULL, NULL); + scratch->d.func.fn_addr = scratch->d.func.fcinfo_data->flinfo->fn_addr; + if (scratch->d.func.finfo->fn_retset) + { + elog(ERROR, "targetlist SRFs are not supported"); + state->failed = true; + return false; + } + + argno = 0; + foreach (lc, args) + { + Expr *arg = (Expr *) lfirst(lc); + + if (IsA(arg, Const)) + { + /* + * Don't evaluate const arguments every round; especially + * interesting for constants in comparisons. + */ + Const *con = (Const *) arg; + + fcinfo->arg[argno] = con->constvalue; + fcinfo->argnull[argno] = con->constisnull; + } + else if (!ExecInitExprRec(arg, parent, econtext, state, &fcinfo->arg[argno], &fcinfo->argnull[argno])) + { + state->failed = true; + return false; + } + argno++; + } + + scratch->d.func.nargs = nargs; + /* + * FIXME: introduce separate opcodes for functioncallusage + * enabled/disabled. + */ + if (pgstat_track_functions <= scratch->d.func.finfo->fn_stats) + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT; + else + scratch->opcode = EEO_FUNCEXPR; + } + else + { + if (scratch->d.func.finfo->fn_strict) + scratch->opcode = EEO_FUNCEXPR_STRICT_FUSAGE; + else + scratch->opcode = EEO_FUNCEXPR_FUSAGE; + } + return true; +} + +static void +ExecInitExprSlots(ExprState2 *state, ExprContext *econtext, Node *node) +{ + ExprEvalStep scratch; + int last_outer = -1; + int last_inner = -1; + int last_scan = -1; + + /* + * Figure out which attributes we're going to need. + */ + ExecGetLastAttnums((Node *) node, + &last_outer, + &last_inner, + &last_scan); + if (last_outer > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_outertuple; + scratch.d.fetch.last_var = last_outer; + ExprEvalPushStep(state, &scratch); + } + if (last_inner > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_innertuple; + scratch.d.fetch.last_var = last_inner; + ExprEvalPushStep(state, &scratch); + } + if (last_scan > 0) + { + scratch.opcode = EEO_FETCHSOMEATTRS; + scratch.d.fetch.slot = &econtext->ecxt_scantuple; + scratch.d.fetch.last_var = last_scan; + ExprEvalPushStep(state, &scratch); + } +} + +ExprState2 * +ExecInitQual(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + if (list_length(qual) > 1) + { + andExpr = makeNode(BoolExpr); + + /* FIXME: ExecQual(false) shortcuts for NULL, this doesn't */ + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + return ExecInitExpr2((Expr *) andExpr, parent, econtext); + } + else + { + return ExecInitExpr2((Expr *) linitial(qual), parent, econtext); + } +} + + +ExprState2 * +ExecInitCheck(List *qual, PlanState *parent, ExprContext *econtext) +{ + BoolExpr *andExpr; + + if (qual == NULL) + return NULL; + + Assert(IsA(qual, List)); + + andExpr = makeNode(BoolExpr); + + andExpr->boolop = AND_EXPR; + andExpr->args = qual; + andExpr->location = -1; + + return ExecInitExpr2((Expr *) andExpr, parent, econtext); +} + +/* ---------------- + * 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, + PlanState *parent, + TupleDesc inputDesc) +{ + ProjectionInfo *projInfo = makeNode(ProjectionInfo); + ExprEvalStep scratch; + ListCell *lc; + ExprState2 *state; + + projInfo->pi_slot = slot; + projInfo->pi_exprContext = econtext; + projInfo->pi_state.econtext = econtext; + projInfo->pi_state.tag.type = T_ExprState2; + state = &projInfo->pi_state; + ExecInitExprSlots(state, econtext, (Node *) targetList); + + foreach(lc, targetList) + { + TargetEntry *tle; + Var *variable = NULL; + AttrNumber attnum; + bool isSimpleVar = false; + + Assert(IsA(lfirst(lc), TargetEntry)); + + tle = (TargetEntry *) lfirst(lc); + + if (tle->expr != NULL && + IsA(tle, Var) && + ((Var *)tle->expr)->varattno > 0) + { + variable = (Var *) tle->expr; + attnum = variable->varattno; + + if (!inputDesc) + isSimpleVar = true; /* can't check type, assume OK */ + else if (variable->varattno <= inputDesc->natts) + { + Form_pg_attribute attr; + + attr = inputDesc->attrs[variable->varattno - 1]; + if (!attr->attisdropped && variable->vartype == attr->atttypid) + isSimpleVar = true; + } + } + + if (isSimpleVar) + { + scratch.opcode = EEO_ASSIGN_VAR; + scratch.d.assign_var.slot = varslot(variable, econtext); + scratch.d.assign_var.attnum = attnum - 1; + scratch.d.assign_var.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + else + { + if (!ExecInitExprRec(tle->expr, parent, econtext, state, &state->resvalue, &state->resnull)) + { + elog(ERROR, "could not compute target list"); + } + else + { + scratch.opcode = EEO_ASSIGN_TMP; + scratch.d.assign_tmp.resultnum = tle->resno - 1; + ExprEvalPushStep(state, &scratch); + } + } + } + + + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return projInfo; +} + + +ExprState2 * +ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *econtext) +{ + ExprState2 *state = makeNode(ExprState2); + ExprEvalStep scratch; + + if (node == NULL) + return NULL; + + state->econtext = econtext; + + ExecInitExprSlots(state, econtext, (Node *) node); + + if (!ExecInitExprRec(node, parent, econtext, state, &state->resvalue, &state->resnull)) + return NULL; + scratch.resvalue = &state->resvalue; + scratch.resnull = &state->resnull; + scratch.opcode = EEO_DONE; + ExprEvalPushStep(state, &scratch); + return state; +} + +static bool +ExecInitExprRec(Expr *node, PlanState *parent, ExprContext *econtext, ExprState2 *state, Datum *resv, bool *resnull) +{ + ExprEvalStep scratch; + + Assert(resv != NULL && resnull != NULL); + scratch.resvalue = resv; + scratch.resnull = resnull; + Assert(econtext != NULL); + + switch (nodeTag(node)) + { + /* suppported */ + case T_Var: + /* varattno == InvalidAttrNumber means it's a whole-row Var */ + if (((Var *) node)->varattno == InvalidAttrNumber) + { + ExecInitOld(state, parent, node, resv, resnull); + } + else + { + Var *variable = (Var *) node; + TupleTableSlot **slot = varslot(variable, econtext); + + /* FIXME: consider re-adding type checks */ + if (variable->varattno <= 0) + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno; + scratch.opcode = EEO_SYSVAR; + } + else + { + scratch.d.var.slot = slot; + scratch.d.var.attnum = variable->varattno - 1; + scratch.opcode = EEO_VAR; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_Const: + { + Const *con = (Const *) node; + + /* FIXME: skip evaluation */ + scratch.opcode = EEO_CONST; + scratch.d.constval.value = con->constvalue; + scratch.d.constval.isnull = con->constisnull; + + ExprEvalPushStep(state, &scratch); + } + break; + case T_FuncExpr: + { + FuncExpr *func = (FuncExpr *) node; + if (!ExecInitFunc2(&scratch, node, func->args, func->funcid, func->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + + ExprEvalPushStep(state, &scratch); + } + break; + case T_OpExpr: + { + OpExpr *op = (OpExpr *) node; + if (!ExecInitFunc2(&scratch, node, op->args, op->opfuncid, op->inputcollid, + parent, econtext, state, resv, resnull)) + { + Assert(false); + return false; + } + ExprEvalPushStep(state, &scratch); + } + break; + case T_BoolExpr: + { + BoolExpr *boolexpr = (BoolExpr *) node; + ListCell *lc; + List *adjust_bailout = NIL; + bool first = true; + + scratch.d.boolexpr.value = palloc0(sizeof(Datum)); + scratch.d.boolexpr.isnull = palloc0(sizeof(bool)); + scratch.d.boolexpr.anynull = palloc0(sizeof(bool)); + + foreach (lc, boolexpr->args) + { + Expr *arg = (Expr *) lfirst(lc); + + switch (boolexpr->boolop) + { + case AND_EXPR: + if (first) + scratch.opcode = EEO_BOOL_AND_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_AND_STEP; + break; + case OR_EXPR: + if (first) + scratch.opcode = EEO_BOOL_OR_STEP_FIRST; + else + scratch.opcode = EEO_BOOL_OR_STEP; + break; + case NOT_EXPR: + Assert(list_length(boolexpr->args) == 1); + scratch.opcode = EEO_BOOL_NOT_STEP; + break; + default: + elog(ERROR, "unrecognized boolop: %d", + (int) boolexpr->boolop); + break; + } + + first = false; + + if (!ExecInitExprRec(arg, parent, econtext, state, + scratch.d.boolexpr.value, + scratch.d.boolexpr.isnull)) + return false; + scratch.d.boolexpr.jumpdone = -1; + ExprEvalPushStep(state, &scratch); + adjust_bailout = lappend_int(adjust_bailout, + state->steps_len - 1); + } + + /* adjust early bail out jump target */ + foreach (lc, adjust_bailout) + { + ExprEvalStep *as = &state->steps[lfirst_int(lc)]; + Assert(as->opcode >= EEO_BOOL_AND_STEP_FIRST && + as->opcode <= EEO_BOOL_NOT_STEP); + Assert(as->d.boolexpr.jumpdone == -1); + as->d.boolexpr.jumpdone = state->steps_len; + } + } + break; + /* unsupported */ + case T_Param: + case T_CoerceToDomainValue: + case T_CaseTestExpr: + case T_Aggref: + case T_GroupingFunc: + case T_WindowFunc: + case T_ArrayRef: + case T_DistinctExpr: + case T_NullIfExpr: + case T_ScalarArrayOpExpr: + case T_SubPlan: + case T_AlternativeSubPlan: + case T_FieldSelect: + case T_FieldStore: + case T_RelabelType: + case T_CoerceViaIO: + case T_ArrayCoerceExpr: + case T_ConvertRowtypeExpr: + case T_CaseExpr: + case T_ArrayExpr: + case T_RowExpr: + case T_RowCompareExpr: + case T_CoalesceExpr: + case T_MinMaxExpr: + case T_XmlExpr: + case T_NullTest: + case T_BooleanTest: + case T_CoerceToDomain: + case T_CurrentOfExpr: + ExecInitOld(state, parent, node, resv, resnull); + break; + case T_TargetEntry: + case T_List: + elog(ERROR, "unexpected"); + default: + elog(ERROR, "unrecognized node type: %d", + (int) nodeTag(node)); + break; + } + return true; +} + +/* + * ExecProjectIntoSlot + * + * projects a tuple based on projection info and stores + * it in the specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +void +ExecProjectIntoSlot(ProjectionInfo *projInfo, TupleTableSlot *slot) +{ + bool isnull; + MemoryContext oldcontext; + ExprContext *econtext; + ExprState2 *state; + + state = &projInfo->pi_state; + econtext = state->econtext; + + /* make conditional? */ + oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + + /* + * Clear any former contents of the result slot. This makes it safe for + * us to use the slot's Datum/isnull arrays as workspace. (Also, we can + * return the slot as-is if we decide no rows can be projected.) + */ + ExecClearTuple(slot); + + state->resultslot = slot; + ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldcontext); + + /* + * Successfully formed a result row. Mark the result slot as containing a + * valid virtual tuple. + */ + ExecStoreVirtualTuple(slot); +} + +bool +ExecQual(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + /* error out? */ + if (state == NULL) + return true; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return false; + return DatumGetBool(ret); +} + + +bool +ExecCheck(ExprState2 *state) +{ + MemoryContext oldContext; + bool isnull; + Datum ret; + + oldContext = MemoryContextSwitchTo(state->econtext->ecxt_per_tuple_memory); + + ret = ExecEvalExpr2(state, &isnull); + + MemoryContextSwitchTo(oldContext); + + if (isnull) + return true; + return DatumGetBool(ret); +} + +/* FIXME: use computed goto only on gcc / clang */ +#define USE_COMPUTED_GOTO + +#ifdef USE_COMPUTED_GOTO +#define EEO_SWITCH(d) +#define EEO_DISPATCH(op) goto *dispatch_table[op->opcode]; +#define EEO_CASE(name) CASE_##name +#else +#define EEO_SWITCH(d) switch (d) +#define EEO_DISPATCH(op) goto starteval +#define EEO_CASE(name) case name +#endif /* USE_COMPUTED_GOTO */ + +Datum +ExecEvalExpr2(ExprState2 *state, bool *isnull) +{ + ExprEvalStep *op = state->steps; + TupleTableSlot *resultslot = state->resultslot; + +#ifdef USE_COMPUTED_GOTO + static const void **dispatch_table[] = { + &&CASE_EEO_VAR, + &&CASE_EEO_SYSVAR, + &&CASE_EEO_CONST, + &&CASE_EEO_FUNCEXPR, + &&CASE_EEO_FUNCEXPR_STRICT, + &&CASE_EEO_FUNCEXPR_FUSAGE, + &&CASE_EEO_FUNCEXPR_STRICT_FUSAGE, + &&CASE_EEO_BOOL_AND_STEP_FIRST, + &&CASE_EEO_BOOL_AND_STEP, + &&CASE_EEO_BOOL_OR_STEP_FIRST, + &&CASE_EEO_BOOL_OR_STEP, + &&CASE_EEO_BOOL_NOT_STEP, + &&CASE_EEO_FETCHSOMEATTRS, + &&CASE_EEO_ASSIGN_VAR, + &&CASE_EEO_ASSIGN_TMP, + &&CASE_EEO_EVAL1, + &&CASE_EEO_ASSIGN_EVAL1, + &&CASE_EEO_DONE + }; +#endif + +#ifdef USE_COMPUTED_GOTO + EEO_DISPATCH(op); +#else +starteval: +#endif + EEO_SWITCH (op->opcode) + { + EEO_CASE(EEO_VAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + /* + * Yuck): can't assert tts_nvalid, wholrow vars or such + * could have materialized the slot - but the contents are + * still valid :/ + * + * Assert(op->d.var.attnum <= slot->tts_nvalid); + */ + Assert(op->d.var.attnum >= 0); + *op->resnull = slot->tts_isnull[attnum]; + *op->resvalue = slot->tts_values[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_SYSVAR): + { + TupleTableSlot *slot = *op->d.var.slot; + int attnum = op->d.var.attnum; + Assert(op->d.var.attnum < 0); + *op->resvalue = heap_getsysattr(slot->tts_tuple, attnum, + slot->tts_tupleDescriptor, + op->resnull); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_CONST): + *op->resnull = op->d.constval.isnull; + *op->resvalue = op->d.constval.value; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT): + { + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail; + } + } + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail: + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_FUNCEXPR_STRICT_FUSAGE): + { + PgStat_FunctionCallUsage fcusage; + FunctionCallInfo fcinfo = op->d.func.fcinfo_data; + int argno; + + /* strict function, check for NULL args */ + for (argno = 0; argno < op->d.func.nargs;argno++) + { + if (fcinfo->argnull[argno]) + { + *op->resnull = true; + goto strictfail_fusage; + } + } + + pgstat_init_function_usage(fcinfo, &fcusage); + + fcinfo->isnull = false; + *op->resvalue = (op->d.func.fn_addr)(fcinfo); + *op->resnull = fcinfo->isnull; + strictfail_fusage: + pgstat_end_function_usage(&fcusage, true); + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_AND_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_AND_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + else if (!DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(false); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_OR_STEP_FIRST): + *op->d.boolexpr.anynull = false; + EEO_CASE(EEO_BOOL_OR_STEP): + *op->resvalue = *op->d.boolexpr.value; + *op->resnull = *op->d.boolexpr.isnull; + + if (*op->d.boolexpr.isnull) + { + *op->d.boolexpr.anynull = true; + } + + if (DatumGetBool(*op->d.boolexpr.value)) + { + *op->resnull = false; + *op->resvalue = BoolGetDatum(true); + /* bail out early */ + op = &state->steps[op->d.boolexpr.jumpdone]; + EEO_DISPATCH(op); + } + + /* XXX: could skip this step till last */ + if (*op->d.boolexpr.anynull) + { + *op->resnull = *op->d.boolexpr.anynull; + *op->resvalue = BoolGetDatum(false); /* or null */ + } + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_BOOL_NOT_STEP): + *op->resvalue = BoolGetDatum(!DatumGetBool(*op->d.boolexpr.value)); + *op->resnull = *op->d.boolexpr.isnull; + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_FETCHSOMEATTRS): + /* XXX: add check for tts_nvalid here? */ + slot_getsomeattrs(*op->d.fetch.slot, + op->d.fetch.last_var); + op++; + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_VAR): + { + size_t resultnum = op->d.assign_var.resultnum; + size_t attnum = op->d.assign_var.attnum; + TupleTableSlot *source = *op->d.assign_var.slot; + resultslot->tts_values[resultnum] = source->tts_values[attnum]; + resultslot->tts_isnull[resultnum] = source->tts_isnull[attnum]; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_ASSIGN_TMP): + { + size_t resultnum = op->d.assign_tmp.resultnum; + resultslot->tts_values[resultnum] = state->resvalue; + resultslot->tts_isnull[resultnum] = state->resnull; + op++; + } + EEO_DISPATCH(op); + EEO_CASE(EEO_EVAL1): + { + *op->resvalue = ExecEvalExpr(op->d.eval1.expr, + state->econtext, + op->resnull, + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_ASSIGN_EVAL1): + { + size_t resultnum = op->d.assign_eval1.resultnum; + resultslot->tts_values[resultnum] = + ExecEvalExpr(op->d.assign_eval1.expr, + state->econtext, + &resultslot->tts_isnull[resultnum], + NULL); + op++; + EEO_DISPATCH(op); + } + EEO_CASE(EEO_DONE): + goto out; + } +out: + *isnull = state->resnull; + return state->resvalue; +} + +#undef EEO_SWITCH +#undef EEO_DISPATCH +#undef EEO_CASE diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 0e2d834..8e1cbba 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -327,23 +327,21 @@ ExecInsertIndexTuples(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } @@ -550,23 +548,22 @@ ExecCheckIndexConstraints(TupleTableSlot *slot, /* Check for partial index */ if (indexInfo->ii_Predicate != NIL) { - List *predicate; + ExprState2 *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)) continue; } diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index 32bb3f9..66003c9 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -1229,7 +1229,7 @@ InitResultRelInfo(ResultRelInfo *resultRelInfo, resultRelInfo->ri_TrigFunctions = (FmgrInfo *) palloc0(n * sizeof(FmgrInfo)); - resultRelInfo->ri_TrigWhenExprs = (List **) + resultRelInfo->ri_TrigWhenExprs = (ExprState2 **) palloc0(n * sizeof(List *)); if (instrument_options) resultRelInfo->ri_TrigInstrument = InstrAlloc(n, instrument_options); @@ -1640,7 +1640,6 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, ConstrCheck *check = rel->rd_att->constr->check; ExprContext *econtext; MemoryContext oldContext; - List *qual; int i; /* @@ -1652,13 +1651,13 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, { oldContext = MemoryContextSwitchTo(estate->es_query_cxt); resultRelInfo->ri_ConstraintExprs = - (List **) palloc(ncheck * sizeof(List *)); + (ExprState2 **) palloc(ncheck * sizeof(ExprState2 *)); for (i = 0; i < ncheck; i++) { + List *qual; /* ExecQual wants implicit-AND form */ qual = make_ands_implicit(stringToNode(check[i].ccbin)); - resultRelInfo->ri_ConstraintExprs[i] = (List *) - ExecPrepareExpr((Expr *) qual, estate); + resultRelInfo->ri_ConstraintExprs[i] = ExecPrepareQual(qual, estate); } MemoryContextSwitchTo(oldContext); } @@ -1675,14 +1674,14 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, /* And evaluate the constraints */ for (i = 0; i < ncheck; i++) { - qual = resultRelInfo->ri_ConstraintExprs[i]; + ExprState2 *qual = resultRelInfo->ri_ConstraintExprs[i]; /* * NOTE: SQL specifies that a NULL result from a constraint expression * is not to be treated as a failure. Therefore, tell ExecQual to * return TRUE for NULL. */ - if (!ExecQual(qual, econtext, true)) + if (!ExecCheck(qual)) return check[i].ccname; } @@ -1793,7 +1792,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, l2, resultRelInfo->ri_WithCheckOptionExprs) { WithCheckOption *wco = (WithCheckOption *) lfirst(l1); - ExprState *wcoExpr = (ExprState *) lfirst(l2); + ExprState2 *wcoExpr = (ExprState2 *) lfirst(l2); /* * Skip any WCOs which are not the kind we are looking for at this @@ -1811,7 +1810,7 @@ ExecWithCheckOptions(WCOKind kind, ResultRelInfo *resultRelInfo, * need ExecQual to return FALSE for NULL to handle the view case (the * opposite of what we do above for CHECK constraints). */ - if (!ExecQual((List *) wcoExpr, econtext, false)) + if (!ExecQual(wcoExpr)) { char *val_desc; Bitmapset *modifiedCols; diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c index d04d1a8..fb070e4 100644 --- a/src/backend/executor/execQual.c +++ b/src/backend/executor/execQual.c @@ -5194,103 +5194,40 @@ ExecPrepareExpr(Expr *node, EState *estate) } +/* + * ExecPrepareQual --- initialize for qual execution outside a normal + * Plan tree context. + * + * This differs from ExecInitExpr in that we don't assume the caller is + * already running in the EState's per-query context. Also, we run the + * passed expression tree through expression_planner() to prepare it for + * execution. (In ordinary Plan trees the regular planning process will have + * made the appropriate transformations on expressions, but for standalone + * expressions this won't have happened.) + */ +ExprState2 * +ExecPrepareQual(List *qual, EState *estate) +{ + ExprState2 *result; + MemoryContext oldcontext; + + oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); + + qual = (List *) expression_planner((Expr *) qual); + + result = ExecInitQual(qual, NULL, GetPerTupleExprContext(estate)); + + MemoryContextSwitchTo(oldcontext); + + return result; +} + + /* ---------------------------------------------------------------- * ExecQual / ExecTargetList / ExecProject * ---------------------------------------------------------------- */ -/* ---------------------------------------------------------------- - * ExecQual - * - * Evaluates a conjunctive boolean expression (qual list) and - * returns true iff none of the subexpressions are false. - * (We also return true if the list is empty.) - * - * If some of the subexpressions yield NULL but none yield FALSE, - * then the result of the conjunction is NULL (ie, unknown) - * according to three-valued boolean logic. In this case, - * we return the value specified by the "resultForNull" parameter. - * - * Callers evaluating WHERE clauses should pass resultForNull=FALSE, - * since SQL specifies that tuples with null WHERE results do not - * get selected. On the other hand, callers evaluating constraint - * conditions should pass resultForNull=TRUE, since SQL also specifies - * that NULL constraint conditions are not failures. - * - * NOTE: it would not be correct to use this routine to evaluate an - * AND subclause of a boolean expression; for that purpose, a NULL - * result must be returned as NULL so that it can be properly treated - * in the next higher operator (cf. ExecEvalAnd and ExecEvalOr). - * This routine is only used in contexts where a complete expression - * is being evaluated and we know that NULL can be treated the same - * as one boolean result or the other. - * - * ---------------------------------------------------------------- - */ -bool -ExecQual(List *qual, ExprContext *econtext, bool resultForNull) -{ - bool result; - MemoryContext oldContext; - ListCell *l; - - /* - * debugging stuff - */ - EV_printf("ExecQual: qual is "); - EV_nodeDisplay(qual); - EV_printf("\n"); - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * Evaluate the qual conditions one at a time. If we find a FALSE result, - * we can stop evaluating and return FALSE --- the AND result must be - * FALSE. Also, if we find a NULL result when resultForNull is FALSE, we - * can stop and return FALSE --- the AND result must be FALSE or NULL in - * that case, and the caller doesn't care which. - * - * If we get to the end of the list, we can return TRUE. This will happen - * when the AND result is indeed TRUE, or when the AND result is NULL (one - * or more NULL subresult, with all the rest TRUE) and the caller has - * specified resultForNull = TRUE. - */ - result = true; - - foreach(l, qual) - { - ExprState *clause = (ExprState *) lfirst(l); - Datum expr_value; - bool isNull; - - expr_value = ExecEvalExpr(clause, econtext, &isNull, NULL); - - if (isNull) - { - if (resultForNull == false) - { - result = false; /* treat NULL as FALSE */ - break; - } - } - else - { - if (!DatumGetBool(expr_value)) - { - result = false; /* definitely FALSE */ - break; - } - } - } - - MemoryContextSwitchTo(oldContext); - - return result; -} - /* * Number of items in a tlist (including any resjunk items!) */ @@ -5320,296 +5257,3 @@ ExecCleanTargetListLength(List *targetlist) } return len; } - -/* - * ExecTargetList - * Evaluates a targetlist with respect to the given - * expression context. Returns TRUE if we were able to create - * a result, FALSE if we have exhausted a set-valued expression. - * - * Results are stored into the passed values and isnull arrays. - * The caller must provide an itemIsDone array that persists across calls. - * - * As with ExecEvalExpr, the caller should pass isDone = NULL if not - * prepared to deal with sets of result tuples. Otherwise, a return - * of *isDone = ExprMultipleResult signifies a set element, and a return - * of *isDone = ExprEndResult signifies end of the set of tuple. - * We assume that *isDone has been initialized to ExprSingleResult by caller. - * - * Since fields of the result tuple might be multiply referenced in higher - * plan nodes, we have to force any read/write expanded values to read-only - * status. It's a bit annoying to have to do that for every projected - * expression; in the future, consider teaching the planner to detect - * actually-multiply-referenced Vars and insert an expression node that - * would do that only where really required. - */ -static bool -ExecTargetList(List *targetlist, - TupleDesc tupdesc, - ExprContext *econtext, - Datum *values, - bool *isnull, - ExprDoneCond *itemIsDone, - ExprDoneCond *isDone) -{ - Form_pg_attribute *att = tupdesc->attrs; - MemoryContext oldContext; - ListCell *tl; - bool haveDoneSets; - - /* - * Run in short-lived per-tuple context while computing expressions. - */ - oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); - - /* - * evaluate all the expressions in the target list - */ - haveDoneSets = false; /* any exhausted set exprs in tlist? */ - - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] != ExprSingleResult) - { - /* We have a set-valued expression in the tlist */ - if (isDone == NULL) - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("set-valued function called in context that cannot accept a set"))); - if (itemIsDone[resind] == ExprMultipleResult) - { - /* we have undone sets in the tlist, set flag */ - *isDone = ExprMultipleResult; - } - else - { - /* we have done sets in the tlist, set flag for that */ - haveDoneSets = true; - } - } - } - - if (haveDoneSets) - { - /* - * note: can't get here unless we verified isDone != NULL - */ - if (*isDone == ExprSingleResult) - { - /* - * all sets are done, so report that tlist expansion is complete. - */ - *isDone = ExprEndResult; - MemoryContextSwitchTo(oldContext); - return false; - } - else - { - /* - * We have some done and some undone sets. Restart the done ones - * so that we can deliver a tuple (if possible). - */ - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - if (itemIsDone[resind] == ExprEndResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - - values[resind] = MakeExpandedObjectReadOnly(values[resind], - isnull[resind], - att[resind]->attlen); - - if (itemIsDone[resind] == ExprEndResult) - { - /* - * Oh dear, this item is returning an empty set. Guess - * we can't make a tuple after all. - */ - *isDone = ExprEndResult; - break; - } - } - } - - /* - * If we cannot make a tuple because some sets are empty, we still - * have to cycle the nonempty sets to completion, else resources - * will not be released from subplans etc. - * - * XXX is that still necessary? - */ - if (*isDone == ExprEndResult) - { - foreach(tl, targetlist) - { - GenericExprState *gstate = (GenericExprState *) lfirst(tl); - TargetEntry *tle = (TargetEntry *) gstate->xprstate.expr; - AttrNumber resind = tle->resno - 1; - - while (itemIsDone[resind] == ExprMultipleResult) - { - values[resind] = ExecEvalExpr(gstate->arg, - econtext, - &isnull[resind], - &itemIsDone[resind]); - /* no need for MakeExpandedObjectReadOnly */ - } - } - - MemoryContextSwitchTo(oldContext); - return false; - } - } - } - - /* Report success */ - MemoryContextSwitchTo(oldContext); - - return true; -} - -/* - * ExecProject - * - * projects a tuple based on projection info and stores - * it in the previously specified tuple table slot. - * - * Note: the result is always a virtual tuple; therefore it - * may reference the contents of the exprContext's scan tuples - * and/or temporary results constructed in the exprContext. - * If the caller wishes the result to be valid longer than that - * data will be valid, he must call ExecMaterializeSlot on the - * result slot. - */ -TupleTableSlot * -ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) -{ - TupleTableSlot *slot; - ExprContext *econtext; - int numSimpleVars; - - /* - * sanity checks - */ - Assert(projInfo != NULL); - - /* - * get the projection info we want - */ - slot = projInfo->pi_slot; - econtext = projInfo->pi_exprContext; - - /* Assume single result row until proven otherwise */ - if (isDone) - *isDone = ExprSingleResult; - - /* - * Clear any former contents of the result slot. This makes it safe for - * us to use the slot's Datum/isnull arrays as workspace. (Also, we can - * return the slot as-is if we decide no rows can be projected.) - */ - ExecClearTuple(slot); - - /* - * Force extraction of all input values that we'll need. The - * Var-extraction loops below depend on this, and we are also prefetching - * all attributes that will be referenced in the generic expressions. - */ - if (projInfo->pi_lastInnerVar > 0) - slot_getsomeattrs(econtext->ecxt_innertuple, - projInfo->pi_lastInnerVar); - if (projInfo->pi_lastOuterVar > 0) - slot_getsomeattrs(econtext->ecxt_outertuple, - projInfo->pi_lastOuterVar); - if (projInfo->pi_lastScanVar > 0) - slot_getsomeattrs(econtext->ecxt_scantuple, - projInfo->pi_lastScanVar); - - /* - * Assign simple Vars to result by direct extraction of fields from source - * slots ... a mite ugly, but fast ... - */ - numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) - { - Datum *values = slot->tts_values; - bool *isnull = slot->tts_isnull; - int *varSlotOffsets = projInfo->pi_varSlotOffsets; - int *varNumbers = projInfo->pi_varNumbers; - int i; - - if (projInfo->pi_directMap) - { - /* especially simple case where vars go to output in order */ - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - - values[i] = varSlot->tts_values[varNumber]; - isnull[i] = varSlot->tts_isnull[varNumber]; - } - } - else - { - /* we have to pay attention to varOutputCols[] */ - int *varOutputCols = projInfo->pi_varOutputCols; - - for (i = 0; i < numSimpleVars; i++) - { - char *slotptr = ((char *) econtext) + varSlotOffsets[i]; - TupleTableSlot *varSlot = *((TupleTableSlot **) slotptr); - int varNumber = varNumbers[i] - 1; - int varOutputCol = varOutputCols[i] - 1; - - values[varOutputCol] = varSlot->tts_values[varNumber]; - isnull[varOutputCol] = varSlot->tts_isnull[varNumber]; - } - } - } - - /* - * If there are any generic expressions, evaluate them. It's possible - * that there are set-returning functions in such expressions; if so and - * we have reached the end of the set, we return the result slot, which we - * already marked empty. - */ - if (projInfo->pi_targetlist) - { - if (!ExecTargetList(projInfo->pi_targetlist, - slot->tts_tupleDescriptor, - econtext, - slot->tts_values, - slot->tts_isnull, - projInfo->pi_itemIsDone, - isDone)) - return slot; /* no more result rows, return empty slot */ - } - - /* - * Successfully formed a result row. Mark the result slot as containing a - * valid virtual tuple. - */ - return ExecStoreVirtualTuple(slot); -} diff --git a/src/backend/executor/execScan.c b/src/backend/executor/execScan.c index fb0013d..1cb33ee 100644 --- a/src/backend/executor/execScan.c +++ b/src/backend/executor/execScan.c @@ -123,7 +123,7 @@ ExecScan(ScanState *node, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; - List *qual; + ExprState2 *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; @@ -205,7 +205,7 @@ ExecScan(ScanState *node, * when the qual is nil ... saves only a few cycles, but they add up * ... */ - if (!qual || ExecQual(qual, econtext, false)) + if (!qual || ExecQual(qual)) { /* * Found a satisfactory scan tuple. diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c index a3a59c0..0fa402f 100644 --- a/src/backend/executor/execUtils.c +++ b/src/backend/executor/execUtils.c @@ -54,7 +54,6 @@ typedef struct LastLastAttnumInfo AttrNumber last_scan; } LastAttnumInfo; - static void ShutdownExprContext(ExprContext *econtext, bool isCommit); @@ -475,142 +474,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 */ - ExecGetLastAttnums((Node *) variable, - &projInfo->pi_lastOuterVar, - &projInfo->pi_lastInnerVar, - &projInfo->pi_lastScanVar); - } - } - projInfo->pi_targetlist = exprlist; - projInfo->pi_numSimpleVars = numSimpleVars; - projInfo->pi_directMap = directMap; - - if (exprlist == NIL) - projInfo->pi_itemIsDone = NULL; /* not needed */ - else - projInfo->pi_itemIsDone = (ExprDoneCond *) - palloc(len * sizeof(ExprDoneCond)); - - return projInfo; -} - /* * get_last_attnums_walker: expression walker for ExecBuildProjectionInfo * @@ -691,9 +554,10 @@ ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc) { planstate->ps_ProjInfo = - ExecBuildProjectionInfo(planstate->targetlist, + ExecBuildProjectionInfo(planstate->plan->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, + planstate, inputDesc); } diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b3187e6..5046da4 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -1571,12 +1571,10 @@ finalize_aggregates(AggState *aggstate, static TupleTableSlot * project_aggregates(AggState *aggstate) { - ExprContext *econtext = aggstate->ss.ps.ps_ExprContext; - /* * 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)) { /* * Form and return or store a projection tuple using the aggregate @@ -2414,9 +2412,10 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) 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, + aggstate->ss.ps.ps_ExprContext); /* * Initialize child nodes. @@ -3094,9 +3093,10 @@ build_pertrans_for_aggref(AggStatePerTrans pertrans, errmsg("aggregate function calls cannot be nested"))); /* Set up projection info for evaluation */ - pertrans->evalproj = ExecBuildProjectionInfo(pertrans->args, + pertrans->evalproj = ExecBuildProjectionInfo(aggref->args, aggstate->tmpcontext, pertrans->evalslot, + (PlanState *) aggstate, NULL); /* diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c index 449aacb..f6cc996 100644 --- a/src/backend/executor/nodeBitmapHeapscan.c +++ b/src/backend/executor/nodeBitmapHeapscan.c @@ -286,7 +286,7 @@ BitmapHeapNext(BitmapHeapScanState *node) econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->bitmapqualorig, econtext, false)) + if (!ExecQual(node->bitmapqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -427,7 +427,7 @@ BitmapHeapRecheck(BitmapHeapScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->bitmapqualorig, econtext, false); + return ExecQual(node->bitmapqualorig); } /* ---------------------------------------------------------------- @@ -583,12 +583,14 @@ ExecInitBitmapHeapScan(BitmapHeapScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->bitmapqualorig = + ExecInitQual(node->bitmapqualorig, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c index 3c2f684..f723f37 100644 --- a/src/backend/executor/nodeCtescan.c +++ b/src/backend/executor/nodeCtescan.c @@ -242,9 +242,10 @@ ExecInitCteScan(CteScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeCustom.c b/src/backend/executor/nodeCustom.c index 322abca..bdd7039 100644 --- a/src/backend/executor/nodeCustom.c +++ b/src/backend/executor/nodeCustom.c @@ -54,9 +54,10 @@ ExecInitCustomScan(CustomScan *cscan, EState *estate, int eflags) 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, + css->ss.ps.ps_ExprContext); /* tuple table initialization */ ExecInitScanTupleSlot(estate, &css->ss); diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c index d886aaf..387320e 100644 --- a/src/backend/executor/nodeForeignscan.c +++ b/src/backend/executor/nodeForeignscan.c @@ -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); } /* ---------------------------------------------------------------- @@ -160,12 +160,14 @@ ExecInitForeignScan(ForeignScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + scanstate->fdw_recheck_quals = + ExecInitQual(node->fdw_recheck_quals, + (PlanState *) scanstate, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c index a03f6e7..47b890a 100644 --- a/src/backend/executor/nodeFunctionscan.c +++ b/src/backend/executor/nodeFunctionscan.c @@ -345,9 +345,10 @@ ExecInitFunctionScan(FunctionScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->funcstates = palloc(nfuncs * sizeof(FunctionScanPerFuncState)); diff --git a/src/backend/executor/nodeGather.c b/src/backend/executor/nodeGather.c index 313b234..1b933ae 100644 --- a/src/backend/executor/nodeGather.c +++ b/src/backend/executor/nodeGather.c @@ -83,9 +83,10 @@ ExecInitGather(Gather *node, EState *estate, int eflags) 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, + gatherstate->ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c index dcf5175..1d61bc2 100644 --- a/src/backend/executor/nodeGroup.c +++ b/src/backend/executor/nodeGroup.c @@ -102,7 +102,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -165,7 +165,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)) { /* * Form and return a projection tuple using the first input tuple. @@ -226,9 +226,10 @@ ExecInitGroup(Group *node, EState *estate, int eflags) 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, + grpstate->ss.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 9ed09a7..85be8c2 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -193,9 +193,10 @@ ExecInitHash(Hash *node, EState *estate, int eflags) 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, + hashstate->ps.ps_ExprContext); /* * initialize child nodes @@ -1064,7 +1065,7 @@ bool ExecScanHashBucket(HashJoinState *hjstate, ExprContext *econtext) { - List *hjclauses = hjstate->hashclauses; + ExprState2 *hjclauses = hjstate->hashclauses; HashJoinTable hashtable = hjstate->hj_HashTable; HashJoinTuple hashTuple = hjstate->hj_CurTuple; uint32 hashvalue = hjstate->hj_CurHashValue; @@ -1098,7 +1099,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)) { hjstate->hj_CurTuple = hashTuple; return true; diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 369e666..72044b6 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -63,8 +63,8 @@ ExecHashJoin(HashJoinState *node) { PlanState *outerNode; HashState *hashNode; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ExprDoneCond isDone; HashJoinTable hashtable; @@ -293,7 +293,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)) { node->hj_MatchedOuter = true; HeapTupleHeaderSetMatch(HJTUPLE_MINTUPLE(node->hj_CurTuple)); @@ -312,8 +312,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)) { TupleTableSlot *result; @@ -351,8 +350,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -390,8 +388,7 @@ ExecHashJoin(HashJoinState *node) */ econtext->ecxt_outertuple = node->hj_NullOuterTupleSlot; - if (otherqual == NIL || - ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { TupleTableSlot *result; @@ -465,16 +462,19 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); + hjstate->hashclauses = + ExecInitQual(node->hashclauses, + (PlanState *) hjstate, + hjstate->js.ps.ps_ExprContext); /* * initialize child nodes @@ -568,16 +568,13 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) lclauses = NIL; rclauses = NIL; hoperators = NIL; - foreach(l, hjstate->hashclauses) + foreach(l, node->hashclauses) { - FuncExprState *fstate = (FuncExprState *) lfirst(l); - OpExpr *hclause; + OpExpr *hclause = (OpExpr *) lfirst(l);; - Assert(IsA(fstate, FuncExprState)); - hclause = (OpExpr *) fstate->xprstate.expr; Assert(IsA(hclause, OpExpr)); - 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; diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c index 4f6f91c..a3e38df 100644 --- a/src/backend/executor/nodeIndexonlyscan.c +++ b/src/backend/executor/nodeIndexonlyscan.c @@ -157,7 +157,7 @@ IndexOnlyNext(IndexOnlyScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqual, econtext, false)) + if (!ExecQual(node->indexqual)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -423,12 +423,14 @@ ExecInitIndexOnlyScan(IndexOnlyScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); + indexstate->indexqual = + ExecInitQual(node->indexqual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c index 3143bd9..b487119 100644 --- a/src/backend/executor/nodeIndexscan.c +++ b/src/backend/executor/nodeIndexscan.c @@ -122,7 +122,7 @@ IndexNext(IndexScanState *node) { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -241,7 +241,7 @@ next_indextuple: { econtext->ecxt_scantuple = slot; ResetExprContext(econtext); - if (!ExecQual(node->indexqualorig, econtext, false)) + if (!ExecQual(node->indexqualorig)) { /* Fails recheck, so drop it and loop back for another */ InstrCountFiltered2(node, 1); @@ -362,7 +362,7 @@ IndexRecheck(IndexScanState *node, TupleTableSlot *slot) ResetExprContext(econtext); - return ExecQual(node->indexqualorig, econtext, false); + return ExecQual(node->indexqualorig); } @@ -852,12 +852,14 @@ ExecInitIndexScan(IndexScan *node, EState *estate, int eflags) 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->ss.ps.qual = + ExecInitQual(node->scan.plan.qual, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); + indexstate->indexqualorig = + ExecInitQual(node->indexqualorig, + (PlanState *) indexstate, + indexstate->ss.ps.ps_ExprContext); indexstate->indexorderbyorig = (List *) ExecInitExpr((Expr *) node->indexorderbyorig, (PlanState *) indexstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 6db09b8..3e7c500 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -452,14 +452,14 @@ static TupleTableSlot * MJFillOuter(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -493,14 +493,14 @@ static TupleTableSlot * MJFillInner(MergeJoinState *node) { ExprContext *econtext = node->js.ps.ps_ExprContext; - List *otherqual = node->js.ps.qual; + ExprState2 *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)) { /* * qualification succeeded. now form the desired projection tuple and @@ -618,8 +618,8 @@ ExecMergeTupleDump(MergeJoinState *mergestate) TupleTableSlot * ExecMergeJoin(MergeJoinState *node) { - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; bool qualResult; int compareResult; PlanState *innerPlan; @@ -823,8 +823,7 @@ ExecMergeJoin(MergeJoinState *node) innerTupleSlot = node->mj_InnerTupleSlot; econtext->ecxt_innertuple = innerTupleSlot; - qualResult = (joinqual == NIL || - ExecQual(joinqual, econtext, false)); + qualResult = (joinqual == NULL || ExecQual(joinqual)); MJ_DEBUG_QUAL(joinqual, qualResult); if (qualResult) @@ -846,8 +845,7 @@ 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)); MJ_DEBUG_QUAL(otherqual, qualResult); if (qualResult) @@ -1507,13 +1505,15 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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->js.ps.ps_ExprContext); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index af7b26c..8255688 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -1067,7 +1067,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, { ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; - List *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; + ExprState2 *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; HeapTupleData tuple; HeapUpdateFailureData hufd; LockTupleMode lockmode; @@ -1186,7 +1186,7 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, econtext->ecxt_innertuple = excludedSlot; econtext->ecxt_outertuple = NULL; - if (!ExecQual(onConflictSetWhere, econtext, false)) + if (!ExecQual(onConflictSetWhere)) { ReleaseBuffer(buffer); InstrCountFiltered1(&mtstate->ps, 1); @@ -1654,8 +1654,9 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags) foreach(ll, wcoList) { WithCheckOption *wco = (WithCheckOption *) lfirst(ll); - ExprState *wcoExpr = ExecInitExpr((Expr *) wco->qual, - mtstate->mt_plans[i]); + ExprState2 *wcoExpr = ExecInitQual((List *) wco->qual, + mtstate->mt_plans[i], + GetPerTupleExprContext(estate)); wcoExprs = lappend(wcoExprs, wcoExpr); } @@ -1687,8 +1688,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. @@ -1697,12 +1699,10 @@ 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, - resultRelInfo->ri_RelationDesc->rd_att); + ExecBuildProjectionInfo(rlist, econtext, slot, &mtstate->ps, + resultRelInfo->ri_RelationDesc->rd_att); resultRelInfo++; } } @@ -1727,7 +1727,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 */ @@ -1753,22 +1752,21 @@ 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 */ if (node->onConflictWhere) { - ExprState *qualexpr; + ExprState2 *qualexpr; - qualexpr = ExecInitExpr((Expr *) node->onConflictWhere, - &mtstate->ps); + qualexpr = ExecInitQual((List *) node->onConflictWhere, + &mtstate->ps, econtext); - resultRelInfo->ri_onConflictSetWhere = (List *) qualexpr; + resultRelInfo->ri_onConflictSetWhere = qualexpr; } } diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index 555fa09..6a8b674 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -64,8 +64,8 @@ ExecNestLoop(NestLoopState *node) PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; - List *joinqual; - List *otherqual; + ExprState2 *joinqual; + ExprState2 *otherqual; ExprContext *econtext; ListCell *lc; @@ -194,7 +194,7 @@ ExecNestLoop(NestLoopState *node) ENL1_printf("testing qualification for outer-join tuple"); - if (otherqual == NIL || ExecQual(otherqual, econtext, false)) + if (otherqual == NULL || ExecQual(otherqual)) { /* * qualification was satisfied so we project and return @@ -202,11 +202,12 @@ ExecNestLoop(NestLoopState *node) * ExecProject(). */ TupleTableSlot *result; - ExprDoneCond isDone; + ExprDoneCond isDone = ExprSingleResult; ENL1_printf("qualification succeeded, projecting tuple"); - result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); + result = node->js.ps.ps_ResultTupleSlot; + ExecProjectIntoSlot(node->js.ps.ps_ProjInfo, result); if (isDone != ExprEndResult) { @@ -235,7 +236,7 @@ ExecNestLoop(NestLoopState *node) */ ENL1_printf("testing qualification"); - if (ExecQual(joinqual, econtext, false)) + if (ExecQual(joinqual)) { node->nl_MatchedOuter = true; @@ -253,7 +254,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)) { /* * qualification was satisfied so we project and return the @@ -323,13 +324,15 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) 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.ps.ps_ExprContext); 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, + nlstate->js.ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c index 4007b76..41d53f2 100644 --- a/src/backend/executor/nodeResult.c +++ b/src/backend/executor/nodeResult.c @@ -79,9 +79,7 @@ ExecResult(ResultState *node) */ if (node->rs_checkqual) { - bool qualResult = ExecQual((List *) node->resconstantqual, - econtext, - false); + bool qualResult = ExecQual(node->resconstantqual); node->rs_checkqual = false; if (!qualResult) @@ -241,11 +239,14 @@ ExecInitResult(Result *node, EState *estate, int eflags) 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->ps.ps_ExprContext); + resstate->resconstantqual = + ExecInitQual((List *) node->resconstantqual, + (PlanState *) resstate, + resstate->ps.ps_ExprContext); /* * initialize child nodes diff --git a/src/backend/executor/nodeSamplescan.c b/src/backend/executor/nodeSamplescan.c index 9ce7c02..6ebc330 100644 --- a/src/backend/executor/nodeSamplescan.c +++ b/src/backend/executor/nodeSamplescan.c @@ -166,9 +166,10 @@ ExecInitSampleScan(SampleScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); scanstate->args = (List *) ExecInitExpr((Expr *) tsc->args, diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c index 00bf3a5..13a84c5 100644 --- a/src/backend/executor/nodeSeqscan.c +++ b/src/backend/executor/nodeSeqscan.c @@ -191,9 +191,10 @@ ExecInitSeqScan(SeqScan *node, EState *estate, int eflags) 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, + scanstate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c index e503494..30c0e33 100644 --- a/src/backend/executor/nodeSubplan.c +++ b/src/backend/executor/nodeSubplan.c @@ -126,7 +126,9 @@ ExecHashSubPlan(SubPlanState *node, /* * Evaluate lefthand expressions and form a projection tuple. First we * have to set the econtext to use (hack alert!). + * FIXME: unlikely to still work */ + elog(ERROR, "borked atm"); node->projLeft->pi_exprContext = econtext; slot = ExecProject(node->projLeft, NULL); @@ -908,21 +910,25 @@ ExecInitSubPlan(SubPlan *subplan, PlanState *parent) * Fortunately we can just pass NULL for now and fill it in later * (hack alert!). The righthand expressions will be evaluated in our * own innerecontext. + * + * FIXME */ tupDesc = ExecTypeFromTL(leftptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projLeft = ExecBuildProjectionInfo(lefttlist, - NULL, + sstate->projLeft = ExecBuildProjectionInfo(leftptlist, + sstate->innerecontext /* FIXME */, slot, + sstate->planstate, NULL); tupDesc = ExecTypeFromTL(rightptlist, false); slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupDesc); - sstate->projRight = ExecBuildProjectionInfo(righttlist, + sstate->projRight = ExecBuildProjectionInfo(rightptlist, sstate->innerecontext, slot, + sstate->planstate, NULL); } diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c index 9bafc62..1e3a228 100644 --- a/src/backend/executor/nodeSubqueryscan.c +++ b/src/backend/executor/nodeSubqueryscan.c @@ -123,9 +123,10 @@ ExecInitSubqueryScan(SubqueryScan *node, EState *estate, int eflags) 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, + subquerystate->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index 2604103..e47e280 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -477,9 +477,10 @@ ExecInitTidScan(TidScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); tidstate->tss_tidquals = (List *) ExecInitExpr((Expr *) node->tidquals, diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c index 9c03f8a..0f3ef05 100644 --- a/src/backend/executor/nodeValuesscan.c +++ b/src/backend/executor/nodeValuesscan.c @@ -246,9 +246,10 @@ ExecInitValuesScan(ValuesScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * get info about values list diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c index d4c88a1..d2dfcb4 100644 --- a/src/backend/executor/nodeWindowAgg.c +++ b/src/backend/executor/nodeWindowAgg.c @@ -1838,7 +1838,7 @@ ExecInitWindowAgg(WindowAgg *node, EState *estate, int eflags) * 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 diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c index cfed6e6..352d1cd 100644 --- a/src/backend/executor/nodeWorktablescan.c +++ b/src/backend/executor/nodeWorktablescan.c @@ -159,9 +159,10 @@ ExecInitWorkTableScan(WorkTableScan *node, EState *estate, int eflags) 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->ss.ps.ps_ExprContext); /* * tuple table initialization diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c index d4a5e7c..4b9d0a2 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -2012,8 +2012,8 @@ setup_privileges(FILE *cmdfd) "UPDATE pg_class " " SET relacl = (SELECT array_agg(a.acl) FROM " " (SELECT E'=r/\"$POSTGRES_SUPERUSERNAME\"' as acl " - " UNION SELECT unnest(pg_catalog.acldefault(" - " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid))" + " UNION SELECT * FROM unnest(pg_catalog.acldefault(" + " CASE WHEN relkind = 'S' THEN 's' ELSE 'r' END::\"char\",10::oid)) un" " ) as a) " " WHERE relkind IN ('r', 'v', 'm', 'S') AND relacl IS NULL;\n\n", "GRANT USAGE ON SCHEMA pg_catalog TO PUBLIC;\n\n", diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e5c0a56..8766d09 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -243,11 +243,41 @@ extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econt bool *isNull, ExprDoneCond *isDone); extern ExprState *ExecInitExpr(Expr *node, PlanState *parent); extern ExprState *ExecPrepareExpr(Expr *node, EState *estate); -extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull); +extern ExprState2 *ExecInitExpr2(Expr *node, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecPrepareQual(List *qual, EState *estate); +extern ExprState2 *ExecPrepareCheck(List *qual, EState *estate); +extern ExprState2 *ExecInitQual(List *qual, PlanState *parent, ExprContext *context); +extern ExprState2 *ExecInitCheck(List *qual, PlanState *parent, ExprContext *context); + +extern Datum ExecEvalExpr2(ExprState2 *state, bool *isnull); +extern bool ExecQual(ExprState2 *state); +extern bool ExecCheck(ExprState2 *state); extern int ExecTargetListLength(List *targetlist); extern int ExecCleanTargetListLength(List *targetlist); -extern TupleTableSlot *ExecProject(ProjectionInfo *projInfo, - ExprDoneCond *isDone); + +extern void ExecProjectIntoSlot(ProjectionInfo *projInfo, + TupleTableSlot *slot); +/* + * ExecProject + * + * projects a tuple based on projection info and stores + * it in the previously specified tuple table slot. + * + * Note: the result is always a virtual tuple; therefore it + * may reference the contents of the exprContext's scan tuples + * and/or temporary results constructed in the exprContext. + * If the caller wishes the result to be valid longer than that + * data will be valid, he must call ExecMaterializeSlot on the + * result slot. + */ +static inline TupleTableSlot * +ExecProject(ProjectionInfo *projInfo, ExprDoneCond *isDone) +{ + if (isDone) + *isDone = ExprSingleResult; + ExecProjectIntoSlot(projInfo, projInfo->pi_slot); + return projInfo->pi_slot; +} /* * prototypes from functions in execScan.c @@ -345,6 +375,7 @@ extern void ExecGetLastAttnums(Node *node, extern ProjectionInfo *ExecBuildProjectionInfo(List *targetList, ExprContext *econtext, TupleTableSlot *slot, + PlanState *planstate, TupleDesc inputDesc); extern void ExecAssignProjectionInfo(PlanState *planstate, TupleDesc inputDesc); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e7fd7bd..b083629 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -26,6 +26,112 @@ #include "utils/tuplesort.h" +typedef struct ExprState ExprState; +typedef struct ExprState2 ExprState2; + + + +typedef enum ExprEvalOp +{ + EEO_VAR, + EEO_SYSVAR, + EEO_CONST, + EEO_FUNCEXPR, + EEO_FUNCEXPR_STRICT, + EEO_FUNCEXPR_FUSAGE, + EEO_FUNCEXPR_STRICT_FUSAGE, + EEO_BOOL_AND_STEP_FIRST, + EEO_BOOL_AND_STEP, + EEO_BOOL_OR_STEP_FIRST, + EEO_BOOL_OR_STEP, + EEO_BOOL_NOT_STEP, + EEO_FETCHSOMEATTRS, + EEO_ASSIGN_VAR, + EEO_ASSIGN_TMP, + EEO_EVAL1, + EEO_ASSIGN_EVAL1, + EEO_DONE +} ExprEvalOp; + +typedef struct ExprEvalStep +{ + ExprEvalOp opcode; + bool *resnull; + Datum *resvalue; + union + { + struct + { + TupleTableSlot **slot; + int attnum; + } var; + + struct + { + Datum value; + bool isnull; + } constval; + + struct + { + FmgrInfo *finfo; + FunctionCallInfo fcinfo_data; + PGFunction fn_addr; + int nargs; + } func; + + struct + { + Datum *value; + bool *isnull; + bool *anynull; + int jumpdone; + } boolexpr; + + struct + { + TupleTableSlot **slot; + size_t resultnum; + int attnum; + } assign_var; + + struct + { + size_t resultnum; + } assign_tmp; + + struct + { + TupleTableSlot **slot; + int last_var; + } fetch; + + struct + { + ExprState *expr; + } eval1; + + struct + { + ExprState *expr; + size_t resultnum; + } assign_eval1; + } d; +} ExprEvalStep; + +struct ExprState2 +{ + Node tag; + bool failed; + size_t steps_alloc; + size_t steps_len; + ExprEvalStep *steps; + Datum resvalue; + bool resnull; + struct ExprContext *econtext; + TupleTableSlot *resultslot; +}; + /* ---------------- * IndexInfo information * @@ -63,7 +169,7 @@ typedef struct IndexInfo List *ii_Expressions; /* list of Expr */ List *ii_ExpressionsState; /* list of ExprState */ List *ii_Predicate; /* list of Expr */ - List *ii_PredicateState; /* list of ExprState */ + ExprState2 *ii_PredicateState; Oid *ii_ExclusionOps; /* array with one entry per column */ Oid *ii_ExclusionProcs; /* array with one entry per column */ uint16 *ii_ExclusionStrats; /* array with one entry per column */ @@ -205,53 +311,16 @@ typedef struct ReturnSetInfo * that is, form new tuples by evaluation of targetlist expressions. * Nodes which need to do projections create one of these. * - * ExecProject() evaluates the tlist, forms a tuple, and stores it - * in the given slot. Note that the result will be a "virtual" tuple - * unless ExecMaterializeSlot() is then called to force it to be - * converted to a physical tuple. The slot must have a tupledesc - * that matches the output of the tlist! - * - * The planner very often produces tlists that consist entirely of - * simple Var references (lower levels of a plan tree almost always - * look like that). And top-level tlists are often mostly Vars too. - * We therefore optimize execution of simple-Var tlist entries. - * The pi_targetlist list actually contains only the tlist entries that - * aren't simple Vars, while those that are Vars are processed using the - * varSlotOffsets/varNumbers/varOutputCols arrays. - * - * The lastXXXVar fields are used to optimize fetching of fields from - * input tuples: they let us do a slot_getsomeattrs() call to ensure - * that all needed attributes are extracted in one pass. - * - * targetlist target list for projection (non-Var expressions only) * exprContext expression context in which to evaluate targetlist * slot slot to place projection result in - * itemIsDone workspace array for ExecProject - * directMap true if varOutputCols[] is an identity map - * numSimpleVars number of simple Vars found in original tlist - * varSlotOffsets array indicating which slot each simple Var is from - * varNumbers array containing input attr numbers of simple Vars - * varOutputCols array containing output attr numbers of simple Vars - * lastInnerVar highest attnum from inner tuple slot (0 if none) - * lastOuterVar highest attnum from outer tuple slot (0 if none) - * lastScanVar highest attnum from scan tuple slot (0 if none) * ---------------- */ typedef struct ProjectionInfo { NodeTag type; - List *pi_targetlist; + ExprState2 pi_state; ExprContext *pi_exprContext; TupleTableSlot *pi_slot; - ExprDoneCond *pi_itemIsDone; - bool pi_directMap; - int pi_numSimpleVars; - int *pi_varSlotOffsets; - int *pi_varNumbers; - int *pi_varOutputCols; - int pi_lastInnerVar; - int pi_lastOuterVar; - int pi_lastScanVar; } ProjectionInfo; /* ---------------- @@ -331,18 +400,18 @@ typedef struct ResultRelInfo IndexInfo **ri_IndexRelationInfo; TriggerDesc *ri_TrigDesc; FmgrInfo *ri_TrigFunctions; - List **ri_TrigWhenExprs; + ExprState2 **ri_TrigWhenExprs; Instrumentation *ri_TrigInstrument; struct FdwRoutine *ri_FdwRoutine; void *ri_FdwState; bool ri_usesFdwDirectModify; List *ri_WithCheckOptions; List *ri_WithCheckOptionExprs; - List **ri_ConstraintExprs; + ExprState2 **ri_ConstraintExprs; JunkFilter *ri_junkFilter; ProjectionInfo *ri_projectReturning; ProjectionInfo *ri_onConflictSetProj; - List *ri_onConflictSetWhere; + ExprState2 *ri_onConflictSetWhere; } ResultRelInfo; /* ---------------- @@ -1039,7 +1108,7 @@ typedef struct PlanState * subPlan list, which does not exist in the plan tree). */ List *targetlist; /* target list to be computed at this node */ - List *qual; /* implicitly-ANDed qual conditions */ + ExprState2 *qual; /* implicitly-ANDed qual conditions */ struct PlanState *lefttree; /* input plan tree(s) */ struct PlanState *righttree; List *initPlan; /* Init SubPlanState nodes (un-correlated expr @@ -1106,7 +1175,7 @@ typedef struct EPQState typedef struct ResultState { PlanState ps; /* its first field is NodeTag */ - ExprState *resconstantqual; + ExprState2 *resconstantqual; bool rs_done; /* are we done? */ bool rs_checkqual; /* do we need to check the qual? */ } ResultState; @@ -1329,7 +1398,7 @@ typedef struct typedef struct IndexScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqualorig; + ExprState2 *indexqualorig; List *indexorderbyorig; ScanKey iss_ScanKeys; int iss_NumScanKeys; @@ -1373,7 +1442,7 @@ typedef struct IndexScanState typedef struct IndexOnlyScanState { ScanState ss; /* its first field is NodeTag */ - List *indexqual; + ExprState2 *indexqual; ScanKey ioss_ScanKeys; int ioss_NumScanKeys; ScanKey ioss_OrderByKeys; @@ -1438,7 +1507,7 @@ typedef struct BitmapIndexScanState typedef struct BitmapHeapScanState { ScanState ss; /* its first field is NodeTag */ - List *bitmapqualorig; + ExprState2 *bitmapqualorig; TIDBitmap *tbm; TBMIterator *tbmiterator; TBMIterateResult *tbmres; @@ -1462,7 +1531,7 @@ typedef struct BitmapHeapScanState typedef struct TidScanState { ScanState ss; /* its first field is NodeTag */ - List *tss_tidquals; /* list of ExprState nodes */ + List *tss_tidquals; bool tss_isCurrentOf; int tss_NumTids; int tss_TidPtr; @@ -1586,7 +1655,7 @@ typedef struct WorkTableScanState typedef struct ForeignScanState { ScanState ss; /* its first field is NodeTag */ - List *fdw_recheck_quals; /* original quals not in ss.ps.qual */ + ExprState2 *fdw_recheck_quals; /* original quals not in ss.ps.qual */ Size pscan_len; /* size of parallel coordination information */ /* use struct pointer to avoid including fdwapi.h here */ struct FdwRoutine *fdwroutine; @@ -1632,7 +1701,7 @@ typedef struct JoinState { PlanState ps; JoinType jointype; - List *joinqual; /* JOIN quals (in addition to ps.qual) */ + ExprState2 *joinqual; /* JOIN quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1730,7 +1799,7 @@ typedef struct HashJoinTableData *HashJoinTable; typedef struct HashJoinState { JoinState js; /* its first field is NodeTag */ - List *hashclauses; /* list of ExprState nodes */ + ExprState2 *hashclauses; List *hj_OuterHashKeys; /* list of ExprState nodes */ List *hj_InnerHashKeys; /* list of ExprState nodes */ List *hj_HashOperators; /* list of operator OIDs */ @@ -1852,6 +1921,12 @@ typedef struct AggState /* these fields are used in AGG_HASHED mode: */ TupleHashTable hashtable; /* hash table with one entry per group */ TupleTableSlot *hashslot; /* slot for loading hash table */ + + /* Stuff for evaluation of agg inputs. */ + TupleTableSlot *evalslot; /* slot for agg inputs */ + ProjectionInfo *evalproj; /* projection machinery */ + TupleDesc evaldesc; /* descriptor of input tuples */ + List *hash_needed; /* list of columns needed in hash table */ bool table_filled; /* hash table filled yet? */ TupleHashIterator hashiter; /* for iterating through hash table */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 6b850e4..0f503d3 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -33,6 +33,7 @@ typedef enum NodeTag T_IndexInfo = 10, T_ExprContext, T_ProjectionInfo, + T_FastProjectionInfo, T_JunkFilter, T_ResultRelInfo, T_EState, @@ -188,6 +189,7 @@ typedef enum NodeTag * from Expr. */ T_ExprState = 400, + T_ExprState2, T_GenericExprState, T_WholeRowVarExprState, T_AggrefExprState, -- 2.8.1 --jfd4diy4zttxubog Content-Type: text/x-patch; charset=us-ascii Content-Disposition: attachment; filename="0004-WIP-Optimize-slot_deform_tuple-significantly.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
* [PATCH v44 04/10] Fix crash caused by involuntary decoding of unrelated relations @ 2026-03-19 18:48 Álvaro Herrera <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Álvaro Herrera @ 2026-03-19 18:48 UTC (permalink / raw) Author: Srinath Reddy Sadipiralla <[email protected]> --- src/backend/commands/cluster.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/backend/commands/cluster.c b/src/backend/commands/cluster.c index edb515d4b3e..b4acaeb6b93 100644 --- a/src/backend/commands/cluster.c +++ b/src/backend/commands/cluster.c @@ -2666,6 +2666,21 @@ repack_setup_logical_decoding(Oid relid) */ Assert(!ctx->fast_forward); + /* Avoid logical decoding of other relations. */ + rel = table_open(relid, AccessShareLock); + repacked_rel_locator = rel->rd_locator; + toastrelid = rel->rd_rel->reltoastrelid; + if (OidIsValid(toastrelid)) + { + Relation toastrel; + + /* Avoid logical decoding of other TOAST relations. */ + toastrel = table_open(toastrelid, AccessShareLock); + repacked_rel_toast_locator = toastrel->rd_locator; + table_close(toastrel, AccessShareLock); + } + table_close(rel, AccessShareLock); + DecodingContextFindStartpoint(ctx); /* @@ -2702,21 +2717,6 @@ repack_setup_logical_decoding(Oid relid) "REPACK - change", ALLOCSET_DEFAULT_SIZES); - /* Avoid logical decoding of other relations. */ - rel = table_open(relid, AccessShareLock); - repacked_rel_locator = rel->rd_locator; - toastrelid = rel->rd_rel->reltoastrelid; - if (OidIsValid(toastrelid)) - { - Relation toastrel; - - /* Avoid logical decoding of other TOAST relations. */ - toastrel = table_open(toastrelid, AccessShareLock); - repacked_rel_toast_locator = toastrel->rd_locator; - table_close(toastrel, AccessShareLock); - } - table_close(rel, AccessShareLock); - /* The file will be set as soon as we have it opened. */ dstate->file = NULL; -- 2.47.3 --gwom7bl7ogtszo4k Content-Type: text/x-diff; charset=utf-8 Content-Disposition: attachment; filename="v44-0005-Rename-ChangeDest-to-ChangeContext-and-absorb-In.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
end of thread, other threads:[~2026-03-19 18:48 UTC | newest] Thread overview: 21+ messages (download: mbox mbox.gz follow: Atom feed) -- links below jump to the message on this page -- 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2016-07-13 04:06 [PATCH 03/20] WIP: Faster expression processing and targetlist projection. Andres Freund <[email protected]> 2026-03-19 18:48 [PATCH v44 04/10] Fix crash caused by involuntary decoding of unrelated relations Álvaro Herrera <[email protected]>
This inbox is served by agora; see mirroring instructions for how to clone and mirror all data and code used for this inbox