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 v7 2/3] Handle default tablespace in AlterTableInternal @ 2025-08-04 22:05 Erik Wienhold <[email protected]> 0 siblings, 0 replies; 21+ messages in thread From: Erik Wienhold @ 2025-08-04 22:05 UTC (permalink / raw) Move handling of default tablespace for CREATE OR REPLACE MATERIALIZED VIEW from create_ctas_internal to ATPrepSetTableSpace. It feels cleaner that way in my opinion by not having to resolve the tablespace name just to pass it to AlterTableInternal. The default table space is passed as empty string to AlterTableInternal. --- src/backend/commands/createas.c | 21 ++------------------- src/backend/commands/tablecmds.c | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 21 deletions(-) diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index 1620273f965..30ca0a21903 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -24,7 +24,6 @@ */ #include "postgres.h" -#include "miscadmin.h" #include "access/heapam.h" #include "access/reloptions.h" #include "access/tableam.h" @@ -35,7 +34,6 @@ #include "commands/matview.h" #include "commands/prepare.h" #include "commands/tablecmds.h" -#include "commands/tablespace.h" #include "commands/view.h" #include "executor/execdesc.h" #include "executor/executor.h" @@ -160,23 +158,8 @@ create_ctas_internal(List *attrList, IntoClause *into) /* tablespace */ atcmd = makeNode(AlterTableCmd); atcmd->subtype = AT_SetTableSpace; - if (into->tableSpaceName != NULL) - atcmd->name = into->tableSpaceName; - else - { - Oid spcid; - - /* - * Resolve the name of the default or database tablespace because - * we need to specify the tablespace by name. - * - * TODO: Move that to ATPrepSetTableSpace? Must allow AlterTableCmd.name to be NULL then. - */ - spcid = GetDefaultTablespace(RELPERSISTENCE_PERMANENT, false); - if (!OidIsValid(spcid)) - spcid = MyDatabaseTableSpace; - atcmd->name = get_tablespace_name(spcid); - } + /* use empty string to specify default tablespace */ + atcmd->name = into->tableSpaceName ? into->tableSpaceName : ""; atcmds = lappend(atcmds, atcmd); /* storage options */ diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 44dcd2c5b0d..6ec0b87f841 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -16580,8 +16580,18 @@ ATPrepSetTableSpace(AlteredTableInfo *tab, Relation rel, const char *tablespacen { Oid tablespaceId; - /* Check that the tablespace exists */ - tablespaceId = get_tablespace_oid(tablespacename, false); + if (tablespacename != NULL && tablespacename[0] == '\0') + { + /* Use default tablespace if name is empty string */ + tablespaceId = GetDefaultTablespace(rel->rd_rel->relpersistence, rel->rd_rel->relispartition); + if (!OidIsValid(tablespaceId)) + tablespaceId = MyDatabaseTableSpace; + } + else + { + /* Check that the tablespace exists */ + tablespaceId = get_tablespace_oid(tablespacename, false); + } /* Check permissions except when moving to database's default */ if (OidIsValid(tablespaceId) && tablespaceId != MyDatabaseTableSpace) -- 2.50.1 --r33ycpluvyfavkwy Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="v7-0003-Add-WITH-OLD-DATA-to-CREATE-OR-REPLACE-MATERIALIZ.patch" ^ permalink raw reply [nested|flat] 21+ messages in thread
end of thread, other threads:[~2025-08-04 22:05 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]> 2025-08-04 22:05 [PATCH v7 2/3] Handle default tablespace in AlterTableInternal Erik Wienhold <[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