cacheexpr-v8.patch
text/x-patch
Filename: cacheexpr-v8.patch
Type: text/x-patch
Part: 0
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
Series: patch v8
| File | + | − |
|---|---|---|
| src/backend/commands/copy.c | 0 | 0 |
| src/backend/commands/tablecmds.c | 0 | 0 |
| src/backend/commands/typecmds.c | 0 | 0 |
| src/backend/executor/execQual.c | 0 | 0 |
| src/backend/executor/nodeAgg.c | 0 | 0 |
| src/backend/executor/nodeBitmapHeapscan.c | 0 | 0 |
| src/backend/executor/nodeCtescan.c | 0 | 0 |
| src/backend/executor/nodeForeignscan.c | 0 | 0 |
| src/backend/executor/nodeFunctionscan.c | 0 | 0 |
| src/backend/executor/nodeGroup.c | 0 | 0 |
| src/backend/executor/nodeHash.c | 0 | 0 |
| src/backend/executor/nodeHashjoin.c | 0 | 0 |
| src/backend/executor/nodeIndexonlyscan.c | 0 | 0 |
| src/backend/executor/nodeIndexscan.c | 0 | 0 |
| src/backend/executor/nodeLimit.c | 0 | 0 |
| src/backend/executor/nodeMergejoin.c | 0 | 0 |
| src/backend/executor/nodeModifyTable.c | 0 | 0 |
| src/backend/executor/nodeNestloop.c | 0 | 0 |
| src/backend/executor/nodeResult.c | 0 | 0 |
| src/backend/executor/nodeSeqscan.c | 0 | 0 |
| src/backend/executor/nodeSubplan.c | 0 | 0 |
| src/backend/executor/nodeSubqueryscan.c | 0 | 0 |
| src/backend/executor/nodeTidscan.c | 0 | 0 |
| src/backend/executor/nodeValuesscan.c | 0 | 0 |
| src/backend/executor/nodeWindowAgg.c | 0 | 0 |
| src/backend/executor/nodeWorktablescan.c | 0 | 0 |
| src/backend/nodes/copyfuncs.c | 0 | 0 |
| src/backend/nodes/equalfuncs.c | 0 | 0 |
| src/backend/nodes/makefuncs.c | 0 | 0 |
| src/backend/nodes/nodeFuncs.c | 0 | 0 |
| src/backend/nodes/outfuncs.c | 0 | 0 |
| src/backend/optimizer/path/costsize.c | 0 | 0 |
| src/backend/optimizer/util/clauses.c | 0 | 0 |
| src/backend/optimizer/util/predtest.c | 0 | 0 |
| src/backend/utils/adt/ruleutils.c | 0 | 0 |
| src/include/executor/executor.h | 0 | 0 |
| src/include/nodes/execnodes.h | 0 | 0 |
| src/include/nodes/makefuncs.h | 0 | 0 |
| src/include/nodes/nodes.h | 0 | 0 |
| src/include/nodes/primnodes.h | 0 | 0 |
| src/pl/plpgsql/src/pl_exec.c | 0 | 0 |
| src/test/regress/expected/cache.out | 0 | 0 |
| src/test/regress/parallel_schedule | 0 | 0 |
| src/test/regress/serial_schedule | 0 | 0 |
| src/test/regress/sql/cache.sql | 0 | 0 |
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
new file mode 100644
index 110480f..a9dbb87
*** a/src/backend/commands/copy.c
--- b/src/backend/commands/copy.c
*************** BeginCopyFrom(Relation rel,
*** 2296,2302 ****
{
/* Initialize expressions in copycontext. */
defexprs[num_defaults] = ExecInitExpr(
! expression_planner((Expr *) defexpr), NULL);
defmap[num_defaults] = attnum - 1;
num_defaults++;
--- 2296,2304 ----
{
/* Initialize expressions in copycontext. */
defexprs[num_defaults] = ExecInitExpr(
! expression_planner((Expr *) defexpr),
! NULL,
! true);
defmap[num_defaults] = attnum - 1;
num_defaults++;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
new file mode 100644
index cd4490a..aa6e141
*** a/src/backend/commands/tablecmds.c
--- b/src/backend/commands/tablecmds.c
*************** ATRewriteTable(AlteredTableInfo *tab, Oi
*** 3575,3581 ****
NewColumnValue *ex = lfirst(l);
/* expr already planned */
! ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL);
}
notnull_attrs = NIL;
--- 3575,3581 ----
NewColumnValue *ex = lfirst(l);
/* expr already planned */
! ex->exprstate = ExecInitExpr((Expr *) ex->expr, NULL, true);
}
notnull_attrs = NIL;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
new file mode 100644
index 22c1132..85fd08a
*** a/src/backend/commands/typecmds.c
--- b/src/backend/commands/typecmds.c
*************** GetDomainConstraints(Oid typeOid)
*** 3049,3055 ****
r = makeNode(DomainConstraintState);
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
! r->check_expr = ExecInitExpr(check_expr, NULL);
/*
* use lcons() here because constraints of lower domains should be
--- 3049,3055 ----
r = makeNode(DomainConstraintState);
r->constrainttype = DOM_CONSTRAINT_CHECK;
r->name = pstrdup(NameStr(c->conname));
! r->check_expr = ExecInitExpr(check_expr, NULL, false);
/*
* use lcons() here because constraints of lower domains should be
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
new file mode 100644
index a1193a8..dd7abeb
*** a/src/backend/executor/execQual.c
--- b/src/backend/executor/execQual.c
***************
*** 51,62 ****
--- 51,70 ----
#include "pgstat.h"
#include "utils/acl.h"
#include "utils/builtins.h"
+ #include "utils/datum.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
#include "utils/xml.h"
+ typedef struct
+ {
+ PlanState *parent;
+ bool useCache;
+ } ExecInitExprContext;
+
+
/* static function decls */
static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
ExprContext *econtext,
*************** static Datum ExecEvalCoerceToDomain(Coer
*** 157,162 ****
--- 165,176 ----
static Datum ExecEvalCoerceToDomainValue(ExprState *exprstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+ static Datum ExecEvalCacheExpr(CacheExprState *cstate,
+ ExprContext *econtext,
+ bool *isNull,ExprDoneCond *isDone);
+ static Datum ExecEvalCacheExprResult(CacheExprState *cstate,
+ ExprContext *econtext,
+ bool *isNull,ExprDoneCond *isDone);
static Datum ExecEvalFieldSelect(FieldSelectState *fstate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
*************** static Datum ExecEvalArrayCoerceExpr(Arr
*** 174,179 ****
--- 188,195 ----
bool *isNull, ExprDoneCond *isDone);
static Datum ExecEvalCurrentOfExpr(ExprState *exprstate, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
+ static ExprState *ExecInitExprMutator(Expr *node,
+ const ExecInitExprContext *context);
/* ----------------------------------------------------------------
*************** ExecEvalBooleanTest(GenericExprState *bs
*** 3754,3759 ****
--- 3770,3832 ----
}
}
+ /* ----------------------------------------------------------------
+ * ExecEvalCacheExpr
+ *
+ * Evaluates a cachable expression for the first time and updates
+ * xprstate.evalfunc to return cached result next time
+ * ----------------------------------------------------------------
+ */
+ static Datum
+ ExecEvalCacheExpr(CacheExprState *cstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+ {
+ MemoryContext oldcontext;
+ Datum result;
+ bool resultTypByVal;
+ int16 resultTypLen;
+ Oid resultType;
+
+ result = ExecEvalExpr(cstate->arg, econtext, isNull, isDone);
+
+ if (!cstate->enabled)
+ return result; /* Cache disabled, pass thru the result */
+
+ /* Set-returning expressions can't be cached */
+ Assert(isDone == NULL || *isDone == ExprSingleResult);
+
+ /* Figure out type and size for copy */
+ resultType = exprType((Node *) ((CacheExpr *) cstate->xprstate.expr)->arg);
+ get_typlenbyval(resultType, &resultTypLen, &resultTypByVal);
+
+ /* This cached datum has to persist for the whole query */
+ oldcontext = MemoryContextSwitchTo(econtext->ecxt_per_query_memory);
+ cstate->result = datumCopy(result, resultTypByVal, resultTypLen);
+ cstate->isNull = *isNull;
+ MemoryContextSwitchTo(oldcontext);
+
+ /* Subsequent calls will return the cached result */
+ cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCacheExprResult;
+
+ return cstate->result;
+ }
+
+ /* ----------------------------------------------------------------
+ * ExecEvalCacheExprResult
+ *
+ * Return the already-cached result, computed in ExecEvalCacheExpr
+ * ----------------------------------------------------------------
+ */
+ static Datum
+ ExecEvalCacheExprResult(CacheExprState *cstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
+ {
+ if (isDone)
+ *isDone = ExprSingleResult;
+ *isNull = cstate->isNull;
+ return cstate->result;
+ }
+
/*
* ExecEvalCoerceToDomain
*
*************** ExecEvalExprSwitchContext(ExprState *exp
*** 4217,4229 ****
*
* 'node' is the root of the expression tree to examine
* 'parent' is the PlanState node that owns the expression.
*
* 'parent' may be NULL if we are preparing an expression that is not
* associated with a plan tree. (If so, it can't have aggs or subplans.)
* This case should usually come through ExecPrepareExpr, not directly here.
*/
ExprState *
! ExecInitExpr(Expr *node, PlanState *parent)
{
ExprState *state;
--- 4290,4319 ----
*
* 'node' is the root of the expression tree to examine
* 'parent' is the PlanState node that owns the expression.
+ * 'useCache' enables caching for stable/parameterized expressions.
*
* 'parent' may be NULL if we are preparing an expression that is not
* associated with a plan tree. (If so, it can't have aggs or subplans.)
* This case should usually come through ExecPrepareExpr, not directly here.
+ *
+ * 'useCache' may only be true if it's guaranteed that all executions of the
+ * expression use the same snapshot and same external params. It should also
+ * be false if the expression is only executed once.
*/
ExprState *
! ExecInitExpr(Expr *node, PlanState *parent, bool useCache)
! {
! ExecInitExprContext context;
!
! context.parent = parent;
! context.useCache = useCache;
!
! return ExecInitExprMutator(node, &context);
! }
!
! static ExprState *
! ExecInitExprMutator(Expr *node,
! const ExecInitExprContext *context)
{
ExprState *state;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4273,4288 ****
AggrefExprState *astate = makeNode(AggrefExprState);
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
! if (parent && IsA(parent, AggState))
{
! AggState *aggstate = (AggState *) parent;
int naggs;
aggstate->aggs = lcons(astate, aggstate->aggs);
naggs = ++aggstate->numaggs;
! astate->args = (List *) ExecInitExpr((Expr *) aggref->args,
! parent);
/*
* Complain if the aggregate's arguments contain any
--- 4363,4378 ----
AggrefExprState *astate = makeNode(AggrefExprState);
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalAggref;
! if (context->parent && IsA(context->parent, AggState))
{
! AggState *aggstate = (AggState *) context->parent;
int naggs;
aggstate->aggs = lcons(astate, aggstate->aggs);
naggs = ++aggstate->numaggs;
! astate->args = (List *)
! ExecInitExprMutator((Expr *) aggref->args, context);
/*
* Complain if the aggregate's arguments contain any
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4309,4317 ****
WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
! if (parent && IsA(parent, WindowAggState))
{
! WindowAggState *winstate = (WindowAggState *) parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
--- 4399,4407 ----
WindowFuncExprState *wfstate = makeNode(WindowFuncExprState);
wfstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalWindowFunc;
! if (context->parent && IsA(context->parent, WindowAggState))
{
! WindowAggState *winstate = (WindowAggState *) context->parent;
int nfuncs;
winstate->funcs = lcons(wfstate, winstate->funcs);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4319,4326 ****
if (wfunc->winagg)
winstate->numaggs++;
! wfstate->args = (List *) ExecInitExpr((Expr *) wfunc->args,
! parent);
/*
* Complain if the windowfunc's arguments contain any
--- 4409,4416 ----
if (wfunc->winagg)
winstate->numaggs++;
! wfstate->args = (List *)
! ExecInitExprMutator((Expr *) wfunc->args, context);
/*
* Complain if the windowfunc's arguments contain any
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4348,4359 ****
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
astate->refupperindexpr = (List *)
! ExecInitExpr((Expr *) aref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
! ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
! astate->refexpr = ExecInitExpr(aref->refexpr, parent);
! astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
! parent);
/* do one-time catalog lookups for type info */
astate->refattrlength = get_typlen(aref->refarraytype);
get_typlenbyvalalign(aref->refelemtype,
--- 4438,4449 ----
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
astate->refupperindexpr = (List *)
! ExecInitExprMutator((Expr *) aref->refupperindexpr, context);
astate->reflowerindexpr = (List *)
! ExecInitExprMutator((Expr *) aref->reflowerindexpr, context);
! astate->refexpr = ExecInitExprMutator(aref->refexpr, context);
! astate->refassgnexpr =
! ExecInitExprMutator(aref->refassgnexpr, context);
/* do one-time catalog lookups for type info */
astate->refattrlength = get_typlen(aref->refarraytype);
get_typlenbyvalalign(aref->refelemtype,
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4370,4376 ****
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
fstate->args = (List *)
! ExecInitExpr((Expr *) funcexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
--- 4460,4466 ----
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFunc;
fstate->args = (List *)
! ExecInitExprMutator((Expr *) funcexpr->args, context);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4382,4388 ****
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
fstate->args = (List *)
! ExecInitExpr((Expr *) opexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
--- 4472,4478 ----
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalOper;
fstate->args = (List *)
! ExecInitExprMutator((Expr *) opexpr->args, context);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4394,4400 ****
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
fstate->args = (List *)
! ExecInitExpr((Expr *) distinctexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
--- 4484,4490 ----
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalDistinct;
fstate->args = (List *)
! ExecInitExprMutator((Expr *) distinctexpr->args, context);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4406,4412 ****
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
fstate->args = (List *)
! ExecInitExpr((Expr *) nullifexpr->args, parent);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
--- 4496,4502 ----
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullIf;
fstate->args = (List *)
! ExecInitExprMutator((Expr *) nullifexpr->args, context);
fstate->func.fn_oid = InvalidOid; /* not initialized */
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4418,4424 ****
sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
sstate->fxprstate.args = (List *)
! ExecInitExpr((Expr *) opexpr->args, parent);
sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
sstate->element_type = InvalidOid; /* ditto */
state = (ExprState *) sstate;
--- 4508,4514 ----
sstate->fxprstate.xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalScalarArrayOp;
sstate->fxprstate.args = (List *)
! ExecInitExprMutator((Expr *) opexpr->args, context);
sstate->fxprstate.func.fn_oid = InvalidOid; /* not initialized */
sstate->element_type = InvalidOid; /* ditto */
state = (ExprState *) sstate;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4446,4452 ****
break;
}
bstate->args = (List *)
! ExecInitExpr((Expr *) boolexpr->args, parent);
state = (ExprState *) bstate;
}
break;
--- 4536,4542 ----
break;
}
bstate->args = (List *)
! ExecInitExprMutator((Expr *) boolexpr->args, context);
state = (ExprState *) bstate;
}
break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4455,4467 ****
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
! if (!parent)
elog(ERROR, "SubPlan found with no parent plan");
! sstate = ExecInitSubPlan(subplan, parent);
/* Add SubPlanState nodes to parent->subPlan */
! parent->subPlan = lappend(parent->subPlan, sstate);
state = (ExprState *) sstate;
}
--- 4545,4558 ----
SubPlan *subplan = (SubPlan *) node;
SubPlanState *sstate;
! if (!context->parent)
elog(ERROR, "SubPlan found with no parent plan");
! sstate = ExecInitSubPlan(subplan, context->parent);
/* Add SubPlanState nodes to parent->subPlan */
! context->parent->subPlan = lappend(context->parent->subPlan,
! sstate);
state = (ExprState *) sstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4471,4480 ****
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
! if (!parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
! asstate = ExecInitAlternativeSubPlan(asplan, parent);
state = (ExprState *) asstate;
}
--- 4562,4571 ----
AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
AlternativeSubPlanState *asstate;
! if (!context->parent)
elog(ERROR, "AlternativeSubPlan found with no parent plan");
! asstate = ExecInitAlternativeSubPlan(asplan, context->parent);
state = (ExprState *) asstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4485,4491 ****
FieldSelectState *fstate = makeNode(FieldSelectState);
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
! fstate->arg = ExecInitExpr(fselect->arg, parent);
fstate->argdesc = NULL;
state = (ExprState *) fstate;
}
--- 4576,4582 ----
FieldSelectState *fstate = makeNode(FieldSelectState);
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldSelect;
! fstate->arg = ExecInitExprMutator(fselect->arg, context);
fstate->argdesc = NULL;
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4496,4503 ****
FieldStoreState *fstate = makeNode(FieldStoreState);
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
! fstate->arg = ExecInitExpr(fstore->arg, parent);
! fstate->newvals = (List *) ExecInitExpr((Expr *) fstore->newvals, parent);
fstate->argdesc = NULL;
state = (ExprState *) fstate;
}
--- 4587,4595 ----
FieldStoreState *fstate = makeNode(FieldStoreState);
fstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalFieldStore;
! fstate->arg = ExecInitExprMutator(fstore->arg, context);
! fstate->newvals = (List *)
! ExecInitExprMutator((Expr *) fstore->newvals, context);
fstate->argdesc = NULL;
state = (ExprState *) fstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4508,4514 ****
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
! gstate->arg = ExecInitExpr(relabel->arg, parent);
state = (ExprState *) gstate;
}
break;
--- 4600,4606 ----
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRelabelType;
! gstate->arg = ExecInitExprMutator(relabel->arg, context);
state = (ExprState *) gstate;
}
break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4520,4526 ****
bool typisvarlena;
iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
! iostate->arg = ExecInitExpr(iocoerce->arg, parent);
/* lookup the result type's input function */
getTypeInputInfo(iocoerce->resulttype, &iofunc,
&iostate->intypioparam);
--- 4612,4618 ----
bool typisvarlena;
iostate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceViaIO;
! iostate->arg = ExecInitExprMutator(iocoerce->arg, context);
/* lookup the result type's input function */
getTypeInputInfo(iocoerce->resulttype, &iofunc,
&iostate->intypioparam);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4538,4544 ****
ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
! astate->arg = ExecInitExpr(acoerce->arg, parent);
astate->resultelemtype = get_element_type(acoerce->resulttype);
if (astate->resultelemtype == InvalidOid)
ereport(ERROR,
--- 4630,4636 ----
ArrayCoerceExprState *astate = makeNode(ArrayCoerceExprState);
astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayCoerceExpr;
! astate->arg = ExecInitExprMutator(acoerce->arg, context);
astate->resultelemtype = get_element_type(acoerce->resulttype);
if (astate->resultelemtype == InvalidOid)
ereport(ERROR,
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4558,4564 ****
ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
! cstate->arg = ExecInitExpr(convert->arg, parent);
state = (ExprState *) cstate;
}
break;
--- 4650,4656 ----
ConvertRowtypeExprState *cstate = makeNode(ConvertRowtypeExprState);
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalConvertRowtype;
! cstate->arg = ExecInitExprMutator(convert->arg, context);
state = (ExprState *) cstate;
}
break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4570,4576 ****
ListCell *l;
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
! cstate->arg = ExecInitExpr(caseexpr->arg, parent);
foreach(l, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(l);
--- 4662,4668 ----
ListCell *l;
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCase;
! cstate->arg = ExecInitExprMutator(caseexpr->arg, context);
foreach(l, caseexpr->args)
{
CaseWhen *when = (CaseWhen *) lfirst(l);
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4579,4590 ****
Assert(IsA(when, CaseWhen));
wstate->xprstate.evalfunc = NULL; /* not used */
wstate->xprstate.expr = (Expr *) when;
! wstate->expr = ExecInitExpr(when->expr, parent);
! wstate->result = ExecInitExpr(when->result, parent);
outlist = lappend(outlist, wstate);
}
cstate->args = outlist;
! cstate->defresult = ExecInitExpr(caseexpr->defresult, parent);
state = (ExprState *) cstate;
}
break;
--- 4671,4683 ----
Assert(IsA(when, CaseWhen));
wstate->xprstate.evalfunc = NULL; /* not used */
wstate->xprstate.expr = (Expr *) when;
! wstate->expr = ExecInitExprMutator(when->expr, context);
! wstate->result = ExecInitExprMutator(when->result, context);
outlist = lappend(outlist, wstate);
}
cstate->args = outlist;
! cstate->defresult =
! ExecInitExprMutator(caseexpr->defresult, context);
state = (ExprState *) cstate;
}
break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4601,4607 ****
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
astate->elements = outlist;
--- 4694,4700 ----
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
astate->elements = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4670,4676 ****
*/
e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
}
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
i++;
}
--- 4763,4769 ----
*/
e = (Expr *) makeNullConst(INT4OID, -1, InvalidOid);
}
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
i++;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4697,4703 ****
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
rstate->largs = outlist;
--- 4790,4796 ----
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
rstate->largs = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4708,4714 ****
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
rstate->rargs = outlist;
--- 4801,4807 ----
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
rstate->rargs = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4761,4767 ****
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
cstate->args = outlist;
--- 4854,4860 ----
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
cstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4782,4788 ****
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
mstate->args = outlist;
--- 4875,4881 ----
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
mstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4819,4825 ****
Expr *e = (Expr *) lfirst(arg);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
xstate->named_args = outlist;
--- 4912,4918 ----
Expr *e = (Expr *) lfirst(arg);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
xstate->named_args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4830,4836 ****
Expr *e = (Expr *) lfirst(arg);
ExprState *estate;
! estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
xstate->args = outlist;
--- 4923,4929 ----
Expr *e = (Expr *) lfirst(arg);
ExprState *estate;
! estate = ExecInitExprMutator(e, context);
outlist = lappend(outlist, estate);
}
xstate->args = outlist;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4844,4850 ****
NullTestState *nstate = makeNode(NullTestState);
nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
! nstate->arg = ExecInitExpr(ntest->arg, parent);
nstate->argdesc = NULL;
state = (ExprState *) nstate;
}
--- 4937,4943 ----
NullTestState *nstate = makeNode(NullTestState);
nstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalNullTest;
! nstate->arg = ExecInitExprMutator(ntest->arg, context);
nstate->argdesc = NULL;
state = (ExprState *) nstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4855,4871 ****
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
! gstate->arg = ExecInitExpr(btest->arg, parent);
state = (ExprState *) gstate;
}
break;
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
! cstate->arg = ExecInitExpr(ctest->arg, parent);
cstate->constraints = GetDomainConstraints(ctest->resulttype);
state = (ExprState *) cstate;
}
--- 4948,4981 ----
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalBooleanTest;
! gstate->arg = ExecInitExprMutator(btest->arg, context);
state = (ExprState *) gstate;
}
break;
+ case T_CacheExpr:
+ {
+ CacheExpr *cache = (CacheExpr *) node;
+ CacheExprState *cstate = makeNode(CacheExprState);
+
+ cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCacheExpr;
+ cstate->arg = ExecInitExprMutator(cache->arg, context);
+ /*
+ * If useCache=false, in theory we could simply skip creating
+ * the CacheExprState node in the first place, but that might
+ * be surprising to future developers.
+ */
+ cstate->enabled = context->useCache;
+
+ state = (ExprState *) cstate;
+ }
+ break;
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
CoerceToDomainState *cstate = makeNode(CoerceToDomainState);
cstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalCoerceToDomain;
! cstate->arg = ExecInitExprMutator(ctest->arg, context);
cstate->constraints = GetDomainConstraints(ctest->resulttype);
state = (ExprState *) cstate;
}
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4880,4886 ****
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = NULL; /* not used */
! gstate->arg = ExecInitExpr(tle->expr, parent);
state = (ExprState *) gstate;
}
break;
--- 4990,4996 ----
GenericExprState *gstate = makeNode(GenericExprState);
gstate->xprstate.evalfunc = NULL; /* not used */
! gstate->arg = ExecInitExprMutator(tle->expr, context);
state = (ExprState *) gstate;
}
break;
*************** ExecInitExpr(Expr *node, PlanState *pare
*** 4892,4899 ****
foreach(l, (List *) node)
{
outlist = lappend(outlist,
! ExecInitExpr((Expr *) lfirst(l),
! parent));
}
/* Don't fall through to the "common" code below */
return (ExprState *) outlist;
--- 5002,5009 ----
foreach(l, (List *) node)
{
outlist = lappend(outlist,
! ExecInitExprMutator((Expr *) lfirst(l),
! context));
}
/* Don't fall through to the "common" code below */
return (ExprState *) outlist;
*************** ExecPrepareExpr(Expr *node, EState *esta
*** 4932,4938 ****
node = expression_planner(node);
! result = ExecInitExpr(node, NULL);
MemoryContextSwitchTo(oldcontext);
--- 5042,5048 ----
node = expression_planner(node);
! result = ExecInitExpr(node, NULL, false);
MemoryContextSwitchTo(oldcontext);
diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c
new file mode 100644
index c9aa921..618a485
*** a/src/backend/executor/nodeAgg.c
--- b/src/backend/executor/nodeAgg.c
*************** ExecInitAgg(Agg *node, EState *estate, i
*** 1444,1453 ****
*/
aggstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) aggstate);
aggstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) aggstate);
/*
* initialize child nodes
--- 1444,1455 ----
*/
aggstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) aggstate,
! true);
aggstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) aggstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeBitmapHeapscan.c b/src/backend/executor/nodeBitmapHeapscan.c
new file mode 100644
index 849665d..f7e2d1c
*** a/src/backend/executor/nodeBitmapHeapscan.c
--- b/src/backend/executor/nodeBitmapHeapscan.c
*************** ExecInitBitmapHeapScan(BitmapHeapScan *n
*** 569,581 ****
*/
scanstate->ss.ps.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);
/*
* tuple table initialization
--- 569,584 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
scanstate->bitmapqualorig = (List *)
ExecInitExpr((Expr *) node->bitmapqualorig,
! (PlanState *) scanstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeCtescan.c b/src/backend/executor/nodeCtescan.c
new file mode 100644
index 7ab15ac..93c2c04
*** a/src/backend/executor/nodeCtescan.c
--- b/src/backend/executor/nodeCtescan.c
*************** ExecInitCteScan(CteScan *node, EState *e
*** 241,250 ****
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate);
/*
* tuple table initialization
--- 241,252 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeForeignscan.c b/src/backend/executor/nodeForeignscan.c
new file mode 100644
index 9204859..1602290
*** a/src/backend/executor/nodeForeignscan.c
--- b/src/backend/executor/nodeForeignscan.c
*************** ExecInitForeignScan(ForeignScan *node, E
*** 129,138 ****
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate);
/*
* tuple table initialization
--- 129,140 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeFunctionscan.c b/src/backend/executor/nodeFunctionscan.c
new file mode 100644
index a700762..955c4ee
*** a/src/backend/executor/nodeFunctionscan.c
--- b/src/backend/executor/nodeFunctionscan.c
*************** ExecInitFunctionScan(FunctionScan *node,
*** 153,162 ****
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate);
/*
* Now determine if the function returns a simple or composite type, and
--- 153,164 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
/*
* Now determine if the function returns a simple or composite type, and
*************** ExecInitFunctionScan(FunctionScan *node,
*** 217,223 ****
*/
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
! (PlanState *) scanstate);
scanstate->ss.ps.ps_TupFromTlist = false;
--- 219,226 ----
*/
scanstate->tuplestorestate = NULL;
scanstate->funcexpr = ExecInitExpr((Expr *) node->funcexpr,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.ps_TupFromTlist = false;
diff --git a/src/backend/executor/nodeGroup.c b/src/backend/executor/nodeGroup.c
new file mode 100644
index 80e282b..881e8d7
*** a/src/backend/executor/nodeGroup.c
--- b/src/backend/executor/nodeGroup.c
*************** ExecInitGroup(Group *node, EState *estat
*** 228,237 ****
*/
grpstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) grpstate);
grpstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) grpstate);
/*
* initialize child nodes
--- 228,239 ----
*/
grpstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) grpstate,
! true);
grpstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) grpstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c
new file mode 100644
index cabe663..7d65e6d
*** a/src/backend/executor/nodeHash.c
--- b/src/backend/executor/nodeHash.c
*************** ExecInitHash(Hash *node, EState *estate,
*** 179,188 ****
*/
hashstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) hashstate);
hashstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) hashstate);
/*
* initialize child nodes
--- 179,190 ----
*/
hashstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) hashstate,
! true);
hashstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) hashstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
new file mode 100644
index 0ea490b..b42ad3f
*** a/src/backend/executor/nodeHashjoin.c
--- b/src/backend/executor/nodeHashjoin.c
*************** ExecInitHashJoin(HashJoin *node, EState
*** 463,479 ****
*/
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.jointype = node->join.jointype;
hjstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) hjstate);
hjstate->hashclauses = (List *)
ExecInitExpr((Expr *) node->hashclauses,
! (PlanState *) hjstate);
/*
* initialize child nodes
--- 463,483 ----
*/
hjstate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
! (PlanState *) hjstate,
! true);
hjstate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
! (PlanState *) hjstate,
! true);
hjstate->js.jointype = node->join.jointype;
hjstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) hjstate,
! true);
hjstate->hashclauses = (List *)
ExecInitExpr((Expr *) node->hashclauses,
! (PlanState *) hjstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeIndexonlyscan.c b/src/backend/executor/nodeIndexonlyscan.c
new file mode 100644
index 4abd805..f20f92f
*** a/src/backend/executor/nodeIndexonlyscan.c
--- b/src/backend/executor/nodeIndexonlyscan.c
*************** ExecInitIndexOnlyScan(IndexOnlyScan *nod
*** 366,378 ****
*/
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);
/*
* tuple table initialization
--- 366,381 ----
*/
indexstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) indexstate,
! true);
indexstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) indexstate,
! true);
indexstate->indexqual = (List *)
ExecInitExpr((Expr *) node->indexqual,
! (PlanState *) indexstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeIndexscan.c b/src/backend/executor/nodeIndexscan.c
new file mode 100644
index e577bac..a2c979d
*** a/src/backend/executor/nodeIndexscan.c
--- b/src/backend/executor/nodeIndexscan.c
*************** ExecInitIndexScan(IndexScan *node, EStat
*** 494,506 ****
*/
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);
/*
* tuple table initialization
--- 494,509 ----
*/
indexstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) indexstate,
! true);
indexstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) indexstate,
! true);
indexstate->indexqualorig = (List *)
ExecInitExpr((Expr *) node->indexqualorig,
! (PlanState *) indexstate,
! true);
/*
* tuple table initialization
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 817,823 ****
}
runtime_keys[n_runtime_keys].scan_key = this_scan_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate);
runtime_keys[n_runtime_keys].key_toastable =
TypeIsToastable(op_righttype);
n_runtime_keys++;
--- 820,826 ----
}
runtime_keys[n_runtime_keys].scan_key = this_scan_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate, true);
runtime_keys[n_runtime_keys].key_toastable =
TypeIsToastable(op_righttype);
n_runtime_keys++;
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 944,950 ****
}
runtime_keys[n_runtime_keys].scan_key = this_sub_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate);
runtime_keys[n_runtime_keys].key_toastable =
TypeIsToastable(op_righttype);
n_runtime_keys++;
--- 947,953 ----
}
runtime_keys[n_runtime_keys].scan_key = this_sub_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate, true);
runtime_keys[n_runtime_keys].key_toastable =
TypeIsToastable(op_righttype);
n_runtime_keys++;
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 1062,1068 ****
}
runtime_keys[n_runtime_keys].scan_key = this_scan_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate);
/*
* Careful here: the runtime expression is not of
--- 1065,1071 ----
}
runtime_keys[n_runtime_keys].scan_key = this_scan_key;
runtime_keys[n_runtime_keys].key_expr =
! ExecInitExpr(rightop, planstate, true);
/*
* Careful here: the runtime expression is not of
*************** ExecIndexBuildScanKeys(PlanState *planst
*** 1080,1086 ****
/* Executor has to expand the array value */
array_keys[n_array_keys].scan_key = this_scan_key;
array_keys[n_array_keys].array_expr =
! ExecInitExpr(rightop, planstate);
/* the remaining fields were zeroed by palloc0 */
n_array_keys++;
scanvalue = (Datum) 0;
--- 1083,1089 ----
/* Executor has to expand the array value */
array_keys[n_array_keys].scan_key = this_scan_key;
array_keys[n_array_keys].array_expr =
! ExecInitExpr(rightop, planstate, true);
/* the remaining fields were zeroed by palloc0 */
n_array_keys++;
scanvalue = (Datum) 0;
diff --git a/src/backend/executor/nodeLimit.c b/src/backend/executor/nodeLimit.c
new file mode 100644
index 8936187..3e2b643
*** a/src/backend/executor/nodeLimit.c
--- b/src/backend/executor/nodeLimit.c
*************** ExecInitLimit(Limit *node, EState *estat
*** 399,407 ****
* initialize child expressions
*/
limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
! (PlanState *) limitstate);
limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
! (PlanState *) limitstate);
/*
* Tuple table initialization (XXX not actually used...)
--- 399,409 ----
* initialize child expressions
*/
limitstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset,
! (PlanState *) limitstate,
! true);
limitstate->limitCount = ExecInitExpr((Expr *) node->limitCount,
! (PlanState *) limitstate,
! true);
/*
* Tuple table initialization (XXX not actually used...)
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
new file mode 100644
index f921847..be45c3e
*** a/src/backend/executor/nodeMergejoin.c
--- b/src/backend/executor/nodeMergejoin.c
*************** MJExamineQuals(List *mergeclauses,
*** 206,213 ****
/*
* Prepare the input expressions for execution.
*/
! clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
! clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
/* Set up sort support data */
clause->ssup.ssup_cxt = CurrentMemoryContext;
--- 206,213 ----
/*
* Prepare the input expressions for execution.
*/
! clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent, true);
! clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent, true);
/* Set up sort support data */
clause->ssup.ssup_cxt = CurrentMemoryContext;
*************** ExecInitMergeJoin(MergeJoin *node, EStat
*** 1498,1511 ****
*/
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.jointype = node->join.jointype;
mergestate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) mergestate);
mergestate->mj_ConstFalseJoin = false;
/* mergeclauses are handled below */
--- 1498,1514 ----
*/
mergestate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
! (PlanState *) mergestate,
! true);
mergestate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
! (PlanState *) mergestate,
! true);
mergestate->js.jointype = node->join.jointype;
mergestate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) mergestate,
! true);
mergestate->mj_ConstFalseJoin = false;
/* mergeclauses are handled below */
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
new file mode 100644
index dfdcb20..3e2810b
*** a/src/backend/executor/nodeModifyTable.c
--- b/src/backend/executor/nodeModifyTable.c
*************** ExecInitModifyTable(ModifyTable *node, E
*** 1003,1009 ****
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);
--- 1003,1009 ----
List *rlist = (List *) lfirst(l);
List *rliststate;
! rliststate = (List *) ExecInitExpr((Expr *) rlist, &mtstate->ps, true);
resultRelInfo->ri_projectReturning =
ExecBuildProjectionInfo(rliststate, econtext, slot,
resultRelInfo->ri_RelationDesc->rd_att);
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
new file mode 100644
index 11d027b..0452478
*** a/src/backend/executor/nodeNestloop.c
--- b/src/backend/executor/nodeNestloop.c
*************** ExecInitNestLoop(NestLoop *node, EState
*** 322,335 ****
*/
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.jointype = node->join.jointype;
nlstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) nlstate);
/*
* initialize child nodes
--- 322,338 ----
*/
nlstate->js.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->join.plan.targetlist,
! (PlanState *) nlstate,
! true);
nlstate->js.ps.qual = (List *)
ExecInitExpr((Expr *) node->join.plan.qual,
! (PlanState *) nlstate,
! true);
nlstate->js.jointype = node->join.jointype;
nlstate->js.joinqual = (List *)
ExecInitExpr((Expr *) node->join.joinqual,
! (PlanState *) nlstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeResult.c b/src/backend/executor/nodeResult.c
new file mode 100644
index b51efd8..5e0869c
*** a/src/backend/executor/nodeResult.c
--- b/src/backend/executor/nodeResult.c
*************** ExecInitResult(Result *node, EState *est
*** 240,251 ****
*/
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);
/*
* initialize child nodes
--- 240,254 ----
*/
resstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) resstate,
! true);
resstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) resstate,
! true);
resstate->resconstantqual = ExecInitExpr((Expr *) node->resconstantqual,
! (PlanState *) resstate,
! true);
/*
* initialize child nodes
diff --git a/src/backend/executor/nodeSeqscan.c b/src/backend/executor/nodeSeqscan.c
new file mode 100644
index 9fcd332..17589fa
*** a/src/backend/executor/nodeSeqscan.c
--- b/src/backend/executor/nodeSeqscan.c
*************** ExecInitSeqScan(SeqScan *node, EState *e
*** 182,191 ****
*/
scanstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) scanstate);
scanstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) scanstate);
/*
* tuple table initialization
--- 182,193 ----
*/
scanstate->ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ps.qual = (List *)
ExecInitExpr((Expr *) node->plan.qual,
! (PlanState *) scanstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeSubplan.c b/src/backend/executor/nodeSubplan.c
new file mode 100644
index da31820..b24a372
*** a/src/backend/executor/nodeSubplan.c
--- b/src/backend/executor/nodeSubplan.c
*************** ExecInitSubPlan(SubPlan *subplan, PlanSt
*** 661,668 ****
subplan->plan_id - 1);
/* Initialize subexpressions */
! sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent);
! sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent);
/*
* initialize my state
--- 661,668 ----
subplan->plan_id - 1);
/* Initialize subexpressions */
! sstate->testexpr = ExecInitExpr((Expr *) subplan->testexpr, parent, true);
! sstate->args = (List *) ExecInitExpr((Expr *) subplan->args, parent, true);
/*
* initialize my state
*************** ExecInitAlternativeSubPlan(AlternativeSu
*** 1102,1108 ****
* we're going to use?)
*/
asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
! parent);
/*
* Select the one to be used. For this, we need an estimate of the number
--- 1102,1109 ----
* we're going to use?)
*/
asstate->subplans = (List *) ExecInitExpr((Expr *) asplan->subplans,
! parent,
! true);
/*
* Select the one to be used. For this, we need an estimate of the number
diff --git a/src/backend/executor/nodeSubqueryscan.c b/src/backend/executor/nodeSubqueryscan.c
new file mode 100644
index b5ef152..040ba5c
*** a/src/backend/executor/nodeSubqueryscan.c
--- b/src/backend/executor/nodeSubqueryscan.c
*************** ExecInitSubqueryScan(SubqueryScan *node,
*** 122,131 ****
*/
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);
/*
* tuple table initialization
--- 122,133 ----
*/
subquerystate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) subquerystate,
! true);
subquerystate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) subquerystate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c
new file mode 100644
index 32b8abf..3842911
*** a/src/backend/executor/nodeTidscan.c
--- b/src/backend/executor/nodeTidscan.c
*************** ExecInitTidScan(TidScan *node, EState *e
*** 506,519 ****
*/
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->tss_tidquals = (List *)
ExecInitExpr((Expr *) node->tidquals,
! (PlanState *) tidstate);
/*
* tuple table initialization
--- 506,522 ----
*/
tidstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) tidstate,
! true);
tidstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) tidstate,
! true);
tidstate->tss_tidquals = (List *)
ExecInitExpr((Expr *) node->tidquals,
! (PlanState *) tidstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/executor/nodeValuesscan.c b/src/backend/executor/nodeValuesscan.c
new file mode 100644
index a6c1b70..6aa99e9
*** a/src/backend/executor/nodeValuesscan.c
--- b/src/backend/executor/nodeValuesscan.c
*************** ValuesNext(ValuesScanState *node)
*** 119,125 ****
* is a SubPlan, and there shouldn't be any (any subselects in the
* VALUES list should be InitPlans).
*/
! exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL);
/* parser should have checked all sublists are the same length */
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
--- 119,125 ----
* is a SubPlan, and there shouldn't be any (any subselects in the
* VALUES list should be InitPlans).
*/
! exprstatelist = (List *) ExecInitExpr((Expr *) exprlist, NULL, true);
/* parser should have checked all sublists are the same length */
Assert(list_length(exprstatelist) == slot->tts_tupleDescriptor->natts);
*************** ExecInitValuesScan(ValuesScan *node, ESt
*** 234,243 ****
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate);
/*
* get info about values list
--- 234,245 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
/*
* get info about values list
diff --git a/src/backend/executor/nodeWindowAgg.c b/src/backend/executor/nodeWindowAgg.c
new file mode 100644
index ce89ff2..2d2c28b
*** a/src/backend/executor/nodeWindowAgg.c
--- b/src/backend/executor/nodeWindowAgg.c
*************** ExecInitWindowAgg(WindowAgg *node, EStat
*** 1455,1461 ****
winstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) winstate);
/*
* WindowAgg nodes never have quals, since they can only occur at the
--- 1455,1462 ----
winstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->plan.targetlist,
! (PlanState *) winstate,
! true);
/*
* WindowAgg nodes never have quals, since they can only occur at the
*************** ExecInitWindowAgg(WindowAgg *node, EStat
*** 1622,1630 ****
/* initialize frame bound offset expressions */
winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
! (PlanState *) winstate);
winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
! (PlanState *) winstate);
winstate->all_first = true;
winstate->partition_spooled = false;
--- 1623,1633 ----
/* initialize frame bound offset expressions */
winstate->startOffset = ExecInitExpr((Expr *) node->startOffset,
! (PlanState *) winstate,
! true);
winstate->endOffset = ExecInitExpr((Expr *) node->endOffset,
! (PlanState *) winstate,
! true);
winstate->all_first = true;
winstate->partition_spooled = false;
diff --git a/src/backend/executor/nodeWorktablescan.c b/src/backend/executor/nodeWorktablescan.c
new file mode 100644
index e2f3dd4..e68d268
*** a/src/backend/executor/nodeWorktablescan.c
--- b/src/backend/executor/nodeWorktablescan.c
*************** ExecInitWorkTableScan(WorkTableScan *nod
*** 160,169 ****
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate);
/*
* tuple table initialization
--- 160,171 ----
*/
scanstate->ss.ps.targetlist = (List *)
ExecInitExpr((Expr *) node->scan.plan.targetlist,
! (PlanState *) scanstate,
! true);
scanstate->ss.ps.qual = (List *)
ExecInitExpr((Expr *) node->scan.plan.qual,
! (PlanState *) scanstate,
! true);
/*
* tuple table initialization
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
new file mode 100644
index 7fec4db..9d1b45a
*** a/src/backend/nodes/copyfuncs.c
--- b/src/backend/nodes/copyfuncs.c
*************** _copyBooleanTest(const BooleanTest *from
*** 1684,1689 ****
--- 1684,1702 ----
}
/*
+ * _copyCacheExpr
+ */
+ static CacheExpr *
+ _copyCacheExpr(const CacheExpr *from)
+ {
+ CacheExpr *newnode = makeNode(CacheExpr);
+
+ COPY_NODE_FIELD(arg);
+
+ return newnode;
+ }
+
+ /*
* _copyCoerceToDomain
*/
static CoerceToDomain *
*************** copyObject(const void *from)
*** 4011,4016 ****
--- 4024,4032 ----
case T_BooleanTest:
retval = _copyBooleanTest(from);
break;
+ case T_CacheExpr:
+ retval = _copyCacheExpr(from);
+ break;
case T_CoerceToDomain:
retval = _copyCoerceToDomain(from);
break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
new file mode 100644
index d2a79eb..710f526
*** a/src/backend/nodes/equalfuncs.c
--- b/src/backend/nodes/equalfuncs.c
*************** _equalBooleanTest(const BooleanTest *a,
*** 678,683 ****
--- 678,691 ----
}
static bool
+ _equalCacheExpr(const CacheExpr *a, const CacheExpr *b)
+ {
+ COMPARE_NODE_FIELD(arg);
+
+ return true;
+ }
+
+ static bool
_equalCoerceToDomain(const CoerceToDomain *a, const CoerceToDomain *b)
{
COMPARE_NODE_FIELD(arg);
*************** equal(const void *a, const void *b)
*** 2567,2572 ****
--- 2575,2583 ----
case T_BooleanTest:
retval = _equalBooleanTest(a, b);
break;
+ case T_CacheExpr:
+ retval = _equalCacheExpr(a, b);
+ break;
case T_CoerceToDomain:
retval = _equalCoerceToDomain(a, b);
break;
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
new file mode 100644
index bb5cdae..c8549eb
*** a/src/backend/nodes/makefuncs.c
--- b/src/backend/nodes/makefuncs.c
*************** makeFuncExpr(Oid funcid, Oid rettype, Li
*** 471,476 ****
--- 471,491 ----
}
/*
+ * makeCacheExpr -
+ * build an expression node for a cachable expression.
+ */
+ CacheExpr *
+ makeCacheExpr(Expr *arg)
+ {
+ CacheExpr *cacheexpr;
+
+ cacheexpr = makeNode(CacheExpr);
+ cacheexpr->arg = arg;
+
+ return cacheexpr;
+ }
+
+ /*
* makeDefElem -
* build a DefElem node
*
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
new file mode 100644
index 51459c4..fab2104
*** a/src/backend/nodes/nodeFuncs.c
--- b/src/backend/nodes/nodeFuncs.c
*************** exprType(const Node *expr)
*** 212,217 ****
--- 212,220 ----
case T_BooleanTest:
type = BOOLOID;
break;
+ case T_CacheExpr:
+ type = exprType((Node *) ((CacheExpr *) expr)->arg);
+ break;
case T_CoerceToDomain:
type = ((const CoerceToDomain *) expr)->resulttype;
break;
*************** exprCollation(const Node *expr)
*** 792,797 ****
--- 795,803 ----
case T_BooleanTest:
coll = InvalidOid; /* result is always boolean */
break;
+ case T_CacheExpr:
+ coll = exprCollation((Node *) ((CacheExpr *) expr)->arg);
+ break;
case T_CoerceToDomain:
coll = ((const CoerceToDomain *) expr)->resultcollid;
break;
*************** exprLocation(const Node *expr)
*** 1263,1268 ****
--- 1269,1278 ----
/* just use argument's location */
loc = exprLocation((Node *) ((const BooleanTest *) expr)->arg);
break;
+ case T_CacheExpr:
+ /* original expression location */
+ loc = exprLocation((Node *) ((CacheExpr *) expr)->arg);
+ break;
case T_CoerceToDomain:
{
const CoerceToDomain *cexpr = (const CoerceToDomain *) expr;
*************** expression_tree_walker(Node *node,
*** 1722,1727 ****
--- 1732,1739 ----
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
return walker(((BooleanTest *) node)->arg, context);
+ case T_CacheExpr:
+ return walker(((CacheExpr *) node)->arg, context);
case T_CoerceToDomain:
return walker(((CoerceToDomain *) node)->arg, context);
case T_TargetEntry:
*************** expression_tree_mutator(Node *node,
*** 2374,2379 ****
--- 2386,2401 ----
return (Node *) newnode;
}
break;
+ case T_CacheExpr:
+ {
+ CacheExpr *cache = (CacheExpr *) node;
+ CacheExpr *newnode;
+
+ FLATCOPY(newnode, cache, CacheExpr);
+ MUTATE(newnode->arg, cache->arg, Expr *);
+ return (Node *) newnode;
+ }
+ break;
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
*************** bool
*** 2781,2786 ****
--- 2803,2810 ----
return walker(((NullTest *) node)->arg, context);
case T_BooleanTest:
return walker(((BooleanTest *) node)->arg, context);
+ case T_CacheExpr:
+ return walker(((CacheExpr *) node)->arg, context);
case T_JoinExpr:
{
JoinExpr *join = (JoinExpr *) node;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
new file mode 100644
index 25a215e..4d59f83
*** a/src/backend/nodes/outfuncs.c
--- b/src/backend/nodes/outfuncs.c
*************** _outBooleanTest(StringInfo str, const Bo
*** 1367,1372 ****
--- 1367,1380 ----
}
static void
+ _outCacheExpr(StringInfo str, const CacheExpr *node)
+ {
+ WRITE_NODE_TYPE("CACHEEXPR");
+
+ WRITE_NODE_FIELD(arg);
+ }
+
+ static void
_outCoerceToDomain(StringInfo str, const CoerceToDomain *node)
{
WRITE_NODE_TYPE("COERCETODOMAIN");
*************** _outNode(StringInfo str, const void *obj
*** 2910,2915 ****
--- 2918,2926 ----
case T_BooleanTest:
_outBooleanTest(str, obj);
break;
+ case T_CacheExpr:
+ _outCacheExpr(str, obj);
+ break;
case T_CoerceToDomain:
_outCoerceToDomain(str, obj);
break;
diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c
new file mode 100644
index 885d855..59514b8
*** a/src/backend/optimizer/path/costsize.c
--- b/src/backend/optimizer/path/costsize.c
*************** cost_qual_eval_walker(Node *node, cost_q
*** 3036,3041 ****
--- 3036,3059 ----
*/
return false; /* don't recurse into children */
}
+ else if (IsA(node, CacheExpr))
+ {
+ cost_qual_eval_context locContext;
+ CacheExpr *cache = (CacheExpr *) node;
+
+ locContext.root = context->root;
+ locContext.total.startup = 0;
+ locContext.total.per_tuple = 0;
+
+ cost_qual_eval_walker((Node *) cache->arg, &locContext);
+
+ /* Account all cached per-tuple costs once in the startup cost. */
+ context->total.startup +=
+ locContext.total.startup + locContext.total.per_tuple;
+
+ /* do NOT recurse into children */
+ return false;
+ }
else if (IsA(node, CoerceViaIO))
{
CoerceViaIO *iocoerce = (CoerceViaIO *) node;
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
new file mode 100644
index cd3da46..cbac4ba
*** a/src/backend/optimizer/util/clauses.c
--- b/src/backend/optimizer/util/clauses.c
*************** static Relids find_nonnullable_rels_walk
*** 98,140 ****
static List *find_nonnullable_vars_walker(Node *node, bool top_level);
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
static bool set_coercionform_dontcare_walker(Node *node, void *context);
! static Node *eval_const_expressions_mutator(Node *node,
! eval_const_expressions_context *context);
static List *simplify_or_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceTrue);
static List *simplify_and_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceFalse);
static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
Oid input_collid, List **args,
- bool has_named_args,
bool allow_inline,
! eval_const_expressions_context *context);
static List *reorder_function_arguments(List *args, Oid result_type,
! HeapTuple func_tuple,
! eval_const_expressions_context *context);
static List *add_function_defaults(List *args, Oid result_type,
! HeapTuple func_tuple,
! eval_const_expressions_context *context);
static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context);
static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context);
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node,
substitute_actual_parameters_context *context);
static void sql_inline_error_callback(void *arg);
static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation);
static Query *substitute_actual_srf_parameters(Query *expr,
--- 98,147 ----
static List *find_nonnullable_vars_walker(Node *node, bool top_level);
static bool is_strict_saop(ScalarArrayOpExpr *expr, bool falseOK);
static bool set_coercionform_dontcare_walker(Node *node, void *context);
! static Node *caching_const_expressions_mutator(Node *node,
! eval_const_expressions_context *context);
! static Node *const_expressions_mutator(Node *node,
! eval_const_expressions_context *context,
! bool *cachable);
static List *simplify_or_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceTrue, bool *cachable);
static List *simplify_and_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceFalse, bool *cachable);
static Node *simplify_boolean_equality(Oid opno, List *args);
static Expr *simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
Oid input_collid, List **args,
bool allow_inline,
! eval_const_expressions_context *context,
! bool *cachable);
! static List *simplify_copy_function_arguments(List *old_args, Oid result_type,
! HeapTuple func_tuple);
static List *reorder_function_arguments(List *args, Oid result_type,
! HeapTuple func_tuple);
static List *add_function_defaults(List *args, Oid result_type,
! HeapTuple func_tuple);
static List *fetch_function_defaults(HeapTuple func_tuple);
static void recheck_cast_function_args(List *args, Oid result_type,
HeapTuple func_tuple);
static Expr *evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context,
! bool *cachable);
static Expr *inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context,
! bool *cachable);
static Node *substitute_actual_parameters(Node *expr, int nargs, List *args,
int *usecounts);
static Node *substitute_actual_parameters_mutator(Node *node,
substitute_actual_parameters_context *context);
static void sql_inline_error_callback(void *arg);
+ static inline bool is_cache_useful(Expr *expr);
+ static Expr *insert_cache(Expr *expr);
static Expr *evaluate_expr(Expr *expr, Oid result_type, int32 result_typmod,
Oid result_collation);
static Query *substitute_actual_srf_parameters(Query *expr,
*************** rowtype_field_matches(Oid rowtypeid, int
*** 2168,2173 ****
--- 2175,2185 ----
* assumption in the presence of user-defined functions; do we need a
* pg_proc flag that prevents discarding the execution of a function?)
*
+ * We also insert CacheExpr nodes above expressions that cannot be
+ * evaluated at planning time, but are constant at execution time.
+ * This includes expressions that contain stable function calls and
+ * Param references.
+ *
* We do understand that certain functions may deliver non-constant
* results even with constant inputs, "nextval()" being the classic
* example. Functions that are not marked "immutable" in pg_proc
*************** eval_const_expressions(PlannerInfo *root
*** 2206,2212 ****
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = false; /* safe transformations only */
! return eval_const_expressions_mutator(node, &context);
}
/*--------------------
--- 2218,2225 ----
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = false; /* safe transformations only */
!
! return caching_const_expressions_mutator(node, &context);
}
/*--------------------
*************** eval_const_expressions(PlannerInfo *root
*** 2224,2235 ****
--- 2237,2255 ----
* value of the Param.
* 2. Fold stable, as well as immutable, functions to constants.
* 3. Reduce PlaceHolderVar nodes to their contained expressions.
+ * 4. Strip CacheExpr nodes, as selectivity estimation functions aren't
+ * prepared to deal with CacheExpr nodes.
+ *
+ * NOTE: This function is not indempotent. Calling it twice over an expression
+ * tree causes CacheExpr nodes to be removed in the first pass, then re-added
+ * in the 2nd pass. Make sure it only gets called once.
*--------------------
*/
Node *
estimate_expression_value(PlannerInfo *root, Node *node)
{
eval_const_expressions_context context;
+ bool isCachable = true;
context.boundParams = root->glob->boundParams; /* bound Params */
/* we do not need to mark the plan as depending on inlined functions */
*************** estimate_expression_value(PlannerInfo *r
*** 2237,2249 ****
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = true; /* unsafe transformations OK */
! return eval_const_expressions_mutator(node, &context);
}
static Node *
! eval_const_expressions_mutator(Node *node,
! eval_const_expressions_context *context)
{
if (node == NULL)
return NULL;
switch (nodeTag(node))
--- 2257,2299 ----
context.active_fns = NIL; /* nothing being recursively simplified */
context.case_val = NULL; /* no CASE being examined */
context.estimate = true; /* unsafe transformations OK */
!
! return const_expressions_mutator(node, &context, &isCachable);
}
+ /*
+ * Calls const_expressions_mutator on the expression tree and automatically
+ * adds a CacheExpr node if the expression is cachable.
+ */
static Node *
! caching_const_expressions_mutator(Node *node,
! eval_const_expressions_context *context)
{
+ bool isCachable = true;
+
+ if (node == NULL)
+ return NULL;
+
+ node = const_expressions_mutator(node, context, &isCachable);
+ if (isCachable)
+ node = (Node *) insert_cache((Expr *) node);
+
+ return node;
+ }
+
+ /*
+ * Returns a mutated node tree and determines its cachability.
+ *
+ * The caller must make sure that cachable points to a boolean value that's
+ * initialized to TRUE.
+ */
+ static Node *
+ const_expressions_mutator(Node *node,
+ eval_const_expressions_context *context,
+ bool *cachable)
+ {
+ Assert(*cachable == true);
+
if (node == NULL)
return NULL;
switch (nodeTag(node))
*************** eval_const_expressions_mutator(Node *nod
*** 2252,2257 ****
--- 2302,2315 ----
{
Param *param = (Param *) node;
+ /*
+ * Only externally-supplied parameters are stable. Other
+ * params are used for passing changing values within the
+ * executor
+ */
+ if (param->paramkind != PARAM_EXTERN)
+ *cachable = false;
+
/* Look to see if we've been given a value for this Param */
if (param->paramkind == PARAM_EXTERN &&
context->boundParams != NULL &&
*************** eval_const_expressions_mutator(Node *nod
*** 2303,2329 ****
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
! List *args;
! bool has_named_args;
Expr *simple;
FuncExpr *newexpr;
- ListCell *lc;
-
- /*
- * Reduce constants in the FuncExpr's arguments, and check to
- * see if there are any named args.
- */
- args = NIL;
- has_named_args = false;
- foreach(lc, expr->args)
- {
- Node *arg = (Node *) lfirst(lc);
-
- arg = eval_const_expressions_mutator(arg, context);
- if (IsA(arg, NamedArgExpr))
- has_named_args = true;
- args = lappend(args, arg);
- }
/*
* Code for op/func reduction is pretty bulky, so split it out
--- 2361,2369 ----
case T_FuncExpr:
{
FuncExpr *expr = (FuncExpr *) node;
! List *args = expr->args;
Expr *simple;
FuncExpr *newexpr;
/*
* Code for op/func reduction is pretty bulky, so split it out
*************** eval_const_expressions_mutator(Node *nod
*** 2339,2347 ****
expr->funccollid,
expr->inputcollid,
&args,
- has_named_args,
true,
! context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2379,2387 ----
expr->funccollid,
expr->inputcollid,
&args,
true,
! context,
! cachable);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2365,2385 ****
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
! List *args;
Expr *simple;
OpExpr *newexpr;
/*
- * Reduce constants in the OpExpr's arguments. We know args
- * is either NIL or a List node, so we can call
- * expression_tree_mutator directly rather than recursing to
- * self.
- */
- args = (List *) expression_tree_mutator((Node *) expr->args,
- eval_const_expressions_mutator,
- (void *) context);
-
- /*
* Need to get OID of underlying function. Okay to scribble
* on input to this extent.
*/
--- 2405,2415 ----
case T_OpExpr:
{
OpExpr *expr = (OpExpr *) node;
! List *args = expr->args;
Expr *simple;
OpExpr *newexpr;
/*
* Need to get OID of underlying function. Okay to scribble
* on input to this extent.
*/
*************** eval_const_expressions_mutator(Node *nod
*** 2395,2401 ****
expr->opcollid,
expr->inputcollid,
&args,
! false, true, context);
if (simple) /* successfully simplified it */
return (Node *) simple;
--- 2425,2432 ----
expr->opcollid,
expr->inputcollid,
&args,
! true, context,
! cachable);
if (simple) /* successfully simplified it */
return (Node *) simple;
*************** eval_const_expressions_mutator(Node *nod
*** 2432,2466 ****
case T_DistinctExpr:
{
DistinctExpr *expr = (DistinctExpr *) node;
! List *args;
! ListCell *arg;
bool has_null_input = false;
bool all_null_input = true;
bool has_nonconst_input = false;
Expr *simple;
DistinctExpr *newexpr;
/*
! * Reduce constants in the DistinctExpr's arguments. We know
! * args is either NIL or a List node, so we can call
! * expression_tree_mutator directly rather than recursing to
! * self.
*/
! args = (List *) expression_tree_mutator((Node *) expr->args,
! eval_const_expressions_mutator,
! (void *) context);
/*
* We must do our own check for NULLs because DistinctExpr has
* different results for NULL input than the underlying
* operator does.
*/
! foreach(arg, args)
{
! if (IsA(lfirst(arg), Const))
{
! has_null_input |= ((Const *) lfirst(arg))->constisnull;
! all_null_input &= ((Const *) lfirst(arg))->constisnull;
}
else
has_nonconst_input = true;
--- 2463,2511 ----
case T_DistinctExpr:
{
DistinctExpr *expr = (DistinctExpr *) node;
! List *args = NIL;
! ListCell *lc;
! Node *arg;
bool has_null_input = false;
bool all_null_input = true;
bool has_nonconst_input = false;
Expr *simple;
DistinctExpr *newexpr;
+ bool leftCachable = true;
+ bool rightCachable = true;
/*
! * Reduce constants in the DistinctExpr's arguments
! *
! * Note that simplify_function() might call the mutator
! * function on arguments for a second time. However, this is
! * harmless because it's only called when arguments are
! * constant.
*/
! Assert(list_length(expr->args) == 2);
!
! arg = const_expressions_mutator(linitial(expr->args),
! context,
! &leftCachable);
! args = lappend(args, arg);
!
! arg = const_expressions_mutator(lsecond(expr->args),
! context,
! &rightCachable);
! args = lappend(args, arg);
/*
* We must do our own check for NULLs because DistinctExpr has
* different results for NULL input than the underlying
* operator does.
*/
! foreach(lc, args)
{
! arg = lfirst(lc);
! if (IsA(arg, Const))
{
! has_null_input |= ((Const *) arg)->constisnull;
! all_null_input &= ((Const *) arg)->constisnull;
}
else
has_nonconst_input = true;
*************** eval_const_expressions_mutator(Node *nod
*** 2497,2503 ****
expr->opcollid,
expr->inputcollid,
&args,
! false, false, context);
if (simple) /* successfully simplified it */
{
/*
--- 2542,2550 ----
expr->opcollid,
expr->inputcollid,
&args,
! false, context,
! cachable);
!
if (simple) /* successfully simplified it */
{
/*
*************** eval_const_expressions_mutator(Node *nod
*** 2512,2517 ****
--- 2559,2590 ----
return (Node *) csimple;
}
}
+ else if (!leftCachable || !rightCachable)
+ {
+ *cachable = false;
+ }
+ else
+ {
+ /*
+ * This expression is only cachable if the equality
+ * operator is not volatile.
+ */
+ HeapTuple func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(expr->opfuncid));
+ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
+
+ if (funcform->provolatile == PROVOLATILE_VOLATILE)
+ *cachable = false;
+
+ ReleaseSysCache(func_tuple);
+ }
+
+ if (!(*cachable))
+ {
+ if (leftCachable)
+ linitial(args) = insert_cache((Expr *) linitial(args));
+ if (rightCachable)
+ lsecond(args) = insert_cache((Expr *) lsecond(args));
+ }
/*
* The expression cannot be simplified any further, so build
*************** eval_const_expressions_mutator(Node *nod
*** 2544,2550 ****
newargs = simplify_or_arguments(expr->args,
context,
&haveNull,
! &forceTrue);
if (forceTrue)
return makeBoolConst(true, false);
if (haveNull)
--- 2617,2624 ----
newargs = simplify_or_arguments(expr->args,
context,
&haveNull,
! &forceTrue,
! cachable);
if (forceTrue)
return makeBoolConst(true, false);
if (haveNull)
*************** eval_const_expressions_mutator(Node *nod
*** 2572,2578 ****
newargs = simplify_and_arguments(expr->args,
context,
&haveNull,
! &forceFalse);
if (forceFalse)
return makeBoolConst(false, false);
if (haveNull)
--- 2646,2653 ----
newargs = simplify_and_arguments(expr->args,
context,
&haveNull,
! &forceFalse,
! cachable);
if (forceFalse)
return makeBoolConst(false, false);
if (haveNull)
*************** eval_const_expressions_mutator(Node *nod
*** 2596,2603 ****
Node *arg;
Assert(list_length(expr->args) == 1);
! arg = eval_const_expressions_mutator(linitial(expr->args),
! context);
/*
* Use negate_clause() to see if we can simplify
--- 2671,2679 ----
Node *arg;
Assert(list_length(expr->args) == 1);
! arg = const_expressions_mutator(linitial(expr->args),
! context,
! cachable);
/*
* Use negate_clause() to see if we can simplify
*************** eval_const_expressions_mutator(Node *nod
*** 2621,2626 ****
--- 2697,2703 ----
* XXX should we ereport() here instead? Probably this routine
* should never be invoked after SubPlan creation.
*/
+ *cachable = false;
return node;
case T_RelabelType:
{
*************** eval_const_expressions_mutator(Node *nod
*** 2633,2640 ****
RelabelType *relabel = (RelabelType *) node;
Node *arg;
! arg = eval_const_expressions_mutator((Node *) relabel->arg,
! context);
/*
* If we find stacked RelabelTypes (eg, from foo :: int ::
--- 2710,2718 ----
RelabelType *relabel = (RelabelType *) node;
Node *arg;
! arg = const_expressions_mutator((Node *) relabel->arg,
! context,
! cachable);
/*
* If we find stacked RelabelTypes (eg, from foo :: int ::
*************** eval_const_expressions_mutator(Node *nod
*** 2668,2674 ****
case T_CoerceViaIO:
{
CoerceViaIO *expr = (CoerceViaIO *) node;
- Expr *arg;
List *args;
Oid outfunc;
bool outtypisvarlena;
--- 2746,2751 ----
*************** eval_const_expressions_mutator(Node *nod
*** 2680,2688 ****
/*
* Reduce constants in the CoerceViaIO's argument.
*/
! arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
! context);
! args = list_make1(arg);
/*
* CoerceViaIO represents calling the source type's output
--- 2757,2763 ----
/*
* Reduce constants in the CoerceViaIO's argument.
*/
! args = list_make1(expr->arg);
/*
* CoerceViaIO represents calling the source type's output
*************** eval_const_expressions_mutator(Node *nod
*** 2693,2699 ****
* Note that the coercion functions are assumed not to care
* about input collation, so we just pass InvalidOid for that.
*/
! getTypeOutputInfo(exprType((Node *) arg),
&outfunc, &outtypisvarlena);
getTypeInputInfo(expr->resulttype,
&infunc, &intypioparam);
--- 2768,2774 ----
* Note that the coercion functions are assumed not to care
* about input collation, so we just pass InvalidOid for that.
*/
! getTypeOutputInfo(exprType((Node *) expr->arg),
&outfunc, &outtypisvarlena);
getTypeInputInfo(expr->resulttype,
&infunc, &intypioparam);
*************** eval_const_expressions_mutator(Node *nod
*** 2704,2710 ****
InvalidOid,
InvalidOid,
&args,
! false, true, context);
if (simple) /* successfully simplified output fn */
{
/*
--- 2779,2786 ----
InvalidOid,
InvalidOid,
&args,
! true, context,
! cachable);
if (simple) /* successfully simplified output fn */
{
/*
*************** eval_const_expressions_mutator(Node *nod
*** 2734,2740 ****
expr->resultcollid,
InvalidOid,
&args,
! false, true, context);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
--- 2810,2817 ----
expr->resultcollid,
InvalidOid,
&args,
! true, context,
! cachable);
if (simple) /* successfully simplified input fn */
return (Node *) simple;
}
*************** eval_const_expressions_mutator(Node *nod
*** 2745,2751 ****
* possibly-simplified argument.
*/
newexpr = makeNode(CoerceViaIO);
! newexpr->arg = arg;
newexpr->resulttype = expr->resulttype;
newexpr->resultcollid = expr->resultcollid;
newexpr->coerceformat = expr->coerceformat;
--- 2822,2828 ----
* possibly-simplified argument.
*/
newexpr = makeNode(CoerceViaIO);
! newexpr->arg = (Expr *) linitial(args);
newexpr->resulttype = expr->resulttype;
newexpr->resultcollid = expr->resultcollid;
newexpr->coerceformat = expr->coerceformat;
*************** eval_const_expressions_mutator(Node *nod
*** 2762,2769 ****
* Reduce constants in the ArrayCoerceExpr's argument, then
* build a new ArrayCoerceExpr.
*/
! arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg,
! context);
newexpr = makeNode(ArrayCoerceExpr);
newexpr->arg = arg;
--- 2839,2847 ----
* Reduce constants in the ArrayCoerceExpr's argument, then
* build a new ArrayCoerceExpr.
*/
! arg = (Expr *) const_expressions_mutator((Node *) expr->arg,
! context,
! cachable);
newexpr = makeNode(ArrayCoerceExpr);
newexpr->arg = arg;
*************** eval_const_expressions_mutator(Node *nod
*** 2786,2791 ****
--- 2864,2879 ----
newexpr->resulttype,
newexpr->resulttypmod,
newexpr->resultcollid);
+ /*
+ * If the argument is cachable, but conversion isn't, insert a
+ * CacheExpr above the argument
+ */
+ if (arg && *cachable && (OidIsValid(newexpr->elemfuncid) &&
+ func_volatile(newexpr->elemfuncid) == PROVOLATILE_VOLATILE))
+ {
+ *cachable = false;
+ newexpr->arg = insert_cache(arg);
+ }
/* Else we must return the partially-simplified node */
return (Node *) newexpr;
*************** eval_const_expressions_mutator(Node *nod
*** 2803,2810 ****
CollateExpr *collate = (CollateExpr *) node;
Node *arg;
! arg = eval_const_expressions_mutator((Node *) collate->arg,
! context);
if (arg && IsA(arg, Const))
{
--- 2891,2899 ----
CollateExpr *collate = (CollateExpr *) node;
Node *arg;
! arg = const_expressions_mutator((Node *) collate->arg,
! context,
! cachable);
if (arg && IsA(arg, Const))
{
*************** eval_const_expressions_mutator(Node *nod
*** 2873,2885 ****
Node *save_case_val;
Node *newarg;
List *newargs;
bool const_true_cond;
Node *defresult = NULL;
ListCell *arg;
/* Simplify the test expression, if any */
! newarg = eval_const_expressions_mutator((Node *) caseexpr->arg,
! context);
/* Set up for contained CaseTestExpr nodes */
save_case_val = context->case_val;
--- 2962,2975 ----
Node *save_case_val;
Node *newarg;
List *newargs;
+ List *cachable_args = NIL;
bool const_true_cond;
Node *defresult = NULL;
ListCell *arg;
/* Simplify the test expression, if any */
! newarg = caching_const_expressions_mutator((Node *) caseexpr->arg,
! context);
/* Set up for contained CaseTestExpr nodes */
save_case_val = context->case_val;
*************** eval_const_expressions_mutator(Node *nod
*** 2899,2910 ****
CaseWhen *oldcasewhen = (CaseWhen *) lfirst(arg);
Node *casecond;
Node *caseresult;
Assert(IsA(oldcasewhen, CaseWhen));
/* Simplify this alternative's test condition */
! casecond = eval_const_expressions_mutator((Node *) oldcasewhen->expr,
! context);
/*
* If the test condition is constant FALSE (or NULL), then
--- 2989,3003 ----
CaseWhen *oldcasewhen = (CaseWhen *) lfirst(arg);
Node *casecond;
Node *caseresult;
+ bool condCachable = true;
+ bool resultCachable = true;
Assert(IsA(oldcasewhen, CaseWhen));
/* Simplify this alternative's test condition */
! casecond = const_expressions_mutator((Node *) oldcasewhen->expr,
! context,
! &condCachable);
/*
* If the test condition is constant FALSE (or NULL), then
*************** eval_const_expressions_mutator(Node *nod
*** 2923,2930 ****
}
/* Simplify this alternative's result value */
! caseresult = eval_const_expressions_mutator((Node *) oldcasewhen->result,
! context);
/* If non-constant test condition, emit a new WHEN node */
if (!const_true_cond)
--- 3016,3024 ----
}
/* Simplify this alternative's result value */
! caseresult = const_expressions_mutator((Node *) oldcasewhen->result,
! context,
! &resultCachable);
/* If non-constant test condition, emit a new WHEN node */
if (!const_true_cond)
*************** eval_const_expressions_mutator(Node *nod
*** 2935,2940 ****
--- 3029,3051 ----
newcasewhen->result = (Expr *) caseresult;
newcasewhen->location = oldcasewhen->location;
newargs = lappend(newargs, newcasewhen);
+
+ if (condCachable)
+ {
+ if (is_cache_useful((Expr *) casecond))
+ cachable_args = lappend(cachable_args, &newcasewhen->expr);
+ }
+ else
+ *cachable = false;
+
+ if (resultCachable)
+ {
+ if (is_cache_useful((Expr *) caseresult))
+ cachable_args = lappend(cachable_args, &newcasewhen->result);
+ }
+ else
+ *cachable = false;
+
continue;
}
*************** eval_const_expressions_mutator(Node *nod
*** 2949,2956 ****
/* Simplify the default result, unless we replaced it above */
if (!const_true_cond)
! defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult,
! context);
context->case_val = save_case_val;
--- 3060,3080 ----
/* Simplify the default result, unless we replaced it above */
if (!const_true_cond)
! {
! bool isCachable = true;
!
! defresult = const_expressions_mutator((Node *) caseexpr->defresult,
! context,
! &isCachable);
!
! if (isCachable)
! {
! if (is_cache_useful((Expr *) defresult))
! cachable_args = lappend(cachable_args, &defresult);
! }
! else
! *cachable = false;
! }
context->case_val = save_case_val;
*************** eval_const_expressions_mutator(Node *nod
*** 2960,2965 ****
--- 3084,3102 ----
*/
if (newargs == NIL)
return defresult;
+
+ if (!(*cachable))
+ {
+ ListCell *lc;
+
+ foreach(lc, cachable_args)
+ {
+ Expr **arg = (Expr **) lfirst(lc);
+
+ *arg = (Expr *) makeCacheExpr(*arg);
+ }
+ }
+
/* Otherwise we need a new CASE node */
newcase = makeNode(CaseExpr);
newcase->casetype = caseexpr->casetype;
*************** eval_const_expressions_mutator(Node *nod
*** 2980,2986 ****
--- 3117,3126 ----
if (context->case_val)
return copyObject(context->case_val);
else
+ {
+ *cachable = false;
return copyObject(node);
+ }
}
case T_ArrayExpr:
{
*************** eval_const_expressions_mutator(Node *nod
*** 2990,3002 ****
List *newelems;
ListCell *element;
newelems = NIL;
foreach(element, arrayexpr->elements)
{
Node *e;
! e = eval_const_expressions_mutator((Node *) lfirst(element),
! context);
if (!IsA(e, Const))
all_const = false;
newelems = lappend(newelems, e);
--- 3130,3144 ----
List *newelems;
ListCell *element;
+ *cachable = false; /* Not implemented */
+
newelems = NIL;
foreach(element, arrayexpr->elements)
{
Node *e;
! e = caching_const_expressions_mutator((Node *) lfirst(element),
! context);
if (!IsA(e, Const))
all_const = false;
newelems = lappend(newelems, e);
*************** eval_const_expressions_mutator(Node *nod
*** 3023,3037 ****
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExpr *newcoalesce;
List *newargs;
! ListCell *arg;
newargs = NIL;
! foreach(arg, coalesceexpr->args)
{
! Node *e;
! e = eval_const_expressions_mutator((Node *) lfirst(arg),
! context);
/*
* We can remove null constants from the list. For a
--- 3165,3182 ----
CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
CoalesceExpr *newcoalesce;
List *newargs;
! List *cachable_args = NIL;
! ListCell *lc;
newargs = NIL;
! foreach(lc, coalesceexpr->args)
{
! Node *arg = lfirst(lc);
! bool isCachable = true;
! arg = const_expressions_mutator((Node *) arg,
! context,
! &isCachable);
/*
* We can remove null constants from the list. For a
*************** eval_const_expressions_mutator(Node *nod
*** 3041,3056 ****
* drop following arguments since they will never be
* reached.
*/
! if (IsA(e, Const))
{
! if (((Const *) e)->constisnull)
continue; /* drop null constant */
if (newargs == NIL)
! return e; /* first expr */
! newargs = lappend(newargs, e);
break;
}
! newargs = lappend(newargs, e);
}
/*
--- 3186,3210 ----
* drop following arguments since they will never be
* reached.
*/
! if (IsA(arg, Const))
{
! if (((Const *) arg)->constisnull)
continue; /* drop null constant */
if (newargs == NIL)
! return arg; /* first expr */
! newargs = lappend(newargs, arg);
break;
}
!
! newargs = lappend(newargs, arg);
!
! if (isCachable)
! {
! if (is_cache_useful((Expr *) arg))
! cachable_args = lappend(cachable_args, &llast(newargs));
! }
! else
! *cachable = false;
}
/*
*************** eval_const_expressions_mutator(Node *nod
*** 3061,3066 ****
--- 3215,3229 ----
return (Node *) makeNullConst(coalesceexpr->coalescetype,
-1,
coalesceexpr->coalescecollid);
+ if (!(*cachable))
+ {
+ foreach(lc, cachable_args)
+ {
+ Expr **arg = (Expr **) lfirst(lc);
+
+ *arg = (Expr *) makeCacheExpr(*arg);
+ }
+ }
newcoalesce = makeNode(CoalesceExpr);
newcoalesce->coalescetype = coalesceexpr->coalescetype;
*************** eval_const_expressions_mutator(Node *nod
*** 3082,3094 ****
* is still the same as when the FieldSelect was created ---
* this can change if someone did ALTER COLUMN TYPE on the
* rowtype.
*/
FieldSelect *fselect = (FieldSelect *) node;
FieldSelect *newfselect;
Node *arg;
! arg = eval_const_expressions_mutator((Node *) fselect->arg,
! context);
if (arg && IsA(arg, Var) &&
((Var *) arg)->varattno == InvalidAttrNumber)
{
--- 3245,3262 ----
* is still the same as when the FieldSelect was created ---
* this can change if someone did ALTER COLUMN TYPE on the
* rowtype.
+ *
+ * This is never cachable because Var references aren't
+ * constants. simplify_function() also refuses caching of
+ * row-returning functions
*/
FieldSelect *fselect = (FieldSelect *) node;
FieldSelect *newfselect;
Node *arg;
! arg = const_expressions_mutator((Node *) fselect->arg,
! context,
! cachable);
if (arg && IsA(arg, Var) &&
((Var *) arg)->varattno == InvalidAttrNumber)
{
*************** eval_const_expressions_mutator(Node *nod
*** 3139,3146 ****
NullTest *newntest;
Node *arg;
! arg = eval_const_expressions_mutator((Node *) ntest->arg,
! context);
if (arg && IsA(arg, RowExpr))
{
/*
--- 3307,3315 ----
NullTest *newntest;
Node *arg;
! arg = const_expressions_mutator((Node *) ntest->arg,
! context,
! cachable);
if (arg && IsA(arg, RowExpr))
{
/*
*************** eval_const_expressions_mutator(Node *nod
*** 3223,3230 ****
BooleanTest *newbtest;
Node *arg;
! arg = eval_const_expressions_mutator((Node *) btest->arg,
! context);
if (arg && IsA(arg, Const))
{
Const *carg = (Const *) arg;
--- 3392,3400 ----
BooleanTest *newbtest;
Node *arg;
! arg = const_expressions_mutator((Node *) btest->arg,
! context,
! cachable);
if (arg && IsA(arg, Const))
{
Const *carg = (Const *) arg;
*************** eval_const_expressions_mutator(Node *nod
*** 3270,3276 ****
return (Node *) newbtest;
}
case T_PlaceHolderVar:
-
/*
* In estimation mode, just strip the PlaceHolderVar node
* altogether; this amounts to estimating that the contained value
--- 3440,3445 ----
*************** eval_const_expressions_mutator(Node *nod
*** 3278,3292 ****
* just use the default behavior (ie, simplify the expression but
* leave the PlaceHolderVar node intact).
*/
if (context->estimate)
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
! return eval_const_expressions_mutator((Node *) phv->phexpr,
! context);
}
break;
default:
break;
}
--- 3447,3483 ----
* just use the default behavior (ie, simplify the expression but
* leave the PlaceHolderVar node intact).
*/
+ *cachable = false;
+
if (context->estimate)
{
PlaceHolderVar *phv = (PlaceHolderVar *) node;
+ bool isCachable = true; /* ignored */
! return const_expressions_mutator((Node *) phv->phexpr,
! context,
! &isCachable);
! }
! break;
! case T_Const:
! /* Keep *cachable=true */
! break;
! case T_CacheExpr:
! /* We already have CacheExpr in the appropriate place.
! * In estimation mode, strip CacheExpr nodes here to simplify
! * selectivity estimation functions.
! */
! *cachable = false;
! if (context->estimate)
! {
! CacheExpr *cache = (CacheExpr *) node;
! bool isCachable = true;
! return const_expressions_mutator((Node *) cache->arg, context, &isCachable);
}
break;
default:
+ /* Everything else is not cachable */
+ *cachable = false;
break;
}
*************** eval_const_expressions_mutator(Node *nod
*** 3294,3303 ****
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
! * cannot eliminate an ArrayRef node, but we might be able to simplify
! * constant expressions in its subscripts.
*/
! return expression_tree_mutator(node, eval_const_expressions_mutator,
(void *) context);
}
--- 3485,3495 ----
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
! * cannot eliminate an ArrayRef node, but we might be able to simplify or
! * cache constant expressions in its subscripts.
*/
! return expression_tree_mutator(node,
! caching_const_expressions_mutator,
(void *) context);
}
*************** eval_const_expressions_mutator(Node *nod
*** 3319,3331 ****
* The output arguments *haveNull and *forceTrue must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or true constant,
* respectively, is detected anywhere in the argument list.
*/
static List *
simplify_or_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceTrue)
{
! List *newargs = NIL;
List *unprocessed_args;
/*
--- 3511,3533 ----
* The output arguments *haveNull and *forceTrue must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or true constant,
* respectively, is detected anywhere in the argument list.
+ *
+ * We divide elements into two separate lists, one for cachable items and one
+ * for non-cachable items. Upon returning, the cachable sub-list is turned
+ * into a new BoolExpr, cached and prepended. This is done in hopes that the
+ * cachable sub-list is faster to evaluate and short-cicruits the rest of the
+ * expression.
+ *
+ * Input: (cachable OR uncachable OR cachable OR uncachable)
+ * Output: (CACHE(cachable OR cachable) OR uncachable OR uncachable)
*/
static List *
simplify_or_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceTrue, bool *cachable)
{
! List *nocache_args = NIL;
! List *cachable_args = NIL;
List *unprocessed_args;
/*
*************** simplify_or_arguments(List *args,
*** 3340,3345 ****
--- 3542,3548 ----
while (unprocessed_args)
{
Node *arg = (Node *) linitial(unprocessed_args);
+ bool isCachable = true;
unprocessed_args = list_delete_first(unprocessed_args);
*************** simplify_or_arguments(List *args,
*** 3362,3368 ****
}
/* If it's not an OR, simplify it */
! arg = eval_const_expressions_mutator(arg, context);
/*
* It is unlikely but not impossible for simplification of a non-OR
--- 3565,3571 ----
}
/* If it's not an OR, simplify it */
! arg = const_expressions_mutator(arg, context, &isCachable);
/*
* It is unlikely but not impossible for simplification of a non-OR
*************** simplify_or_arguments(List *args,
*** 3404,3413 ****
}
/* else emit the simplified arg into the result list */
! newargs = lappend(newargs, arg);
}
! return newargs;
}
/*
--- 3607,3646 ----
}
/* else emit the simplified arg into the result list */
! if (isCachable)
! cachable_args = lappend(cachable_args, arg);
! else
! nocache_args = lappend(nocache_args, arg);
}
! if (cachable_args && nocache_args)
! {
! Expr *arg;
!
! /* Build a new expression for cachable sub-list */
! if (list_length(cachable_args) == 1)
! arg = linitial(cachable_args);
! else
! arg = makeBoolExpr(OR_EXPR, cachable_args, -1);
!
! arg = insert_cache(arg);
!
! /*
! * Assume that the cachable expression is cheaper to evaluate, so put
! * it first
! */
! nocache_args = lcons(arg, nocache_args);
!
! *cachable = false;
! return nocache_args;
! }
! else if (nocache_args)
! {
! *cachable = false;
! return nocache_args;
! }
! else
! return cachable_args;
}
/*
*************** simplify_or_arguments(List *args,
*** 3428,3440 ****
* The output arguments *haveNull and *forceFalse must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or false constant,
* respectively, is detected anywhere in the argument list.
*/
static List *
simplify_and_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceFalse)
{
! List *newargs = NIL;
List *unprocessed_args;
/* See comments in simplify_or_arguments */
--- 3661,3683 ----
* The output arguments *haveNull and *forceFalse must be initialized FALSE
* by the caller. They will be set TRUE if a null constant or false constant,
* respectively, is detected anywhere in the argument list.
+ *
+ * We divide elements into two separate lists, one for cachable items and one
+ * for non-cachable items. Upon returning, the cachable sub-list is turned
+ * into a new BoolExpr, cached and prepended. This is done in hopes that the
+ * cachable sub-list is faster to evaluate and short-cicruits the rest of the
+ * expression.
+ *
+ * Input: (cachable OR uncachable OR cachable OR uncachable)
+ * Output: (CACHE(cachable OR cachable) OR uncachable OR uncachable)
*/
static List *
simplify_and_arguments(List *args,
eval_const_expressions_context *context,
! bool *haveNull, bool *forceFalse, bool *cachable)
{
! List *nocache_args = NIL;
! List *cachable_args = NIL;
List *unprocessed_args;
/* See comments in simplify_or_arguments */
*************** simplify_and_arguments(List *args,
*** 3442,3447 ****
--- 3685,3691 ----
while (unprocessed_args)
{
Node *arg = (Node *) linitial(unprocessed_args);
+ bool isCachable = true;
unprocessed_args = list_delete_first(unprocessed_args);
*************** simplify_and_arguments(List *args,
*** 3464,3470 ****
}
/* If it's not an AND, simplify it */
! arg = eval_const_expressions_mutator(arg, context);
/*
* It is unlikely but not impossible for simplification of a non-AND
--- 3708,3714 ----
}
/* If it's not an AND, simplify it */
! arg = const_expressions_mutator(arg, context, &isCachable);
/*
* It is unlikely but not impossible for simplification of a non-AND
*************** simplify_and_arguments(List *args,
*** 3506,3515 ****
}
/* else emit the simplified arg into the result list */
! newargs = lappend(newargs, arg);
}
! return newargs;
}
/*
--- 3750,3789 ----
}
/* else emit the simplified arg into the result list */
! if (isCachable)
! cachable_args = lappend(cachable_args, arg);
! else
! nocache_args = lappend(nocache_args, arg);
}
! if (cachable_args && nocache_args)
! {
! Expr *arg;
!
! /* Build a new expression for cachable sub-list */
! if (list_length(cachable_args) == 1)
! arg = linitial(cachable_args);
! else
! arg = makeBoolExpr(AND_EXPR, cachable_args, -1);
!
! arg = insert_cache(arg);
!
! /*
! * Assume that the cachable expression is cheaper to evaluate, so put
! * it first
! */
! nocache_args = lcons(arg, nocache_args);
!
! *cachable = false;
! return nocache_args;
! }
! else if (nocache_args)
! {
! *cachable = false;
! return nocache_args;
! }
! else
! return cachable_args;
}
/*
*************** simplify_boolean_equality(Oid opno, List
*** 3584,3590 ****
* Inputs are the original expression (can be NULL), function OID, actual
* result type OID (which is needed for polymorphic functions), result typmod,
* result collation, the input collation to use for the function, the
! * pre-simplified argument list, and some flags; also the context data for
* eval_const_expressions. In common cases, several of the arguments could be
* derived from the original expression. Sending them separately avoids
* duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
--- 3858,3864 ----
* Inputs are the original expression (can be NULL), function OID, actual
* result type OID (which is needed for polymorphic functions), result typmod,
* result collation, the input collation to use for the function, the
! * un-simplified argument list, and some flags; also the context data for
* eval_const_expressions. In common cases, several of the arguments could be
* derived from the original expression. Sending them separately avoids
* duplicating NodeTag-specific knowledge, and it's necessary for CoerceViaIO.
*************** simplify_boolean_equality(Oid opno, List
*** 3603,3616 ****
static Expr *
simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
! Oid input_collid, List **args,
! bool has_named_args,
bool allow_inline,
! eval_const_expressions_context *context)
{
HeapTuple func_tuple;
Expr *newexpr;
Oid transform;
/*
* We have three strategies for simplification: execute the function to
--- 3877,3893 ----
static Expr *
simplify_function(Expr *oldexpr, Oid funcid,
Oid result_type, int32 result_typmod, Oid result_collid,
! Oid input_collid, List **old_args,
bool allow_inline,
! eval_const_expressions_context *context,
! bool *cachable)
{
HeapTuple func_tuple;
Expr *newexpr;
Oid transform;
+ ListCell *lc;
+ List *args = NIL;
+ List *cachable_args = NIL;
/*
* We have three strategies for simplification: execute the function to
*************** simplify_function(Expr *oldexpr, Oid fun
*** 3624,3642 ****
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
/*
! * While we have the tuple, reorder named arguments and add default
! * arguments if needed.
*/
- if (has_named_args)
- *args = reorder_function_arguments(*args, result_type, func_tuple,
- context);
- else if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(*args))
- *args = add_function_defaults(*args, result_type, func_tuple, context);
-
newexpr = evaluate_function(funcid, result_type, result_typmod,
! result_collid, input_collid, *args,
! func_tuple, context);
/*
* Some functions calls can be simplified at plan time based on properties
--- 3901,3953 ----
if (!HeapTupleIsValid(func_tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
+ if (oldexpr && IsA(oldexpr, FuncExpr))
+
+ /*
+ * Reorder named arguments and add defaults if needed. Returns a
+ * copied list, so we can mutate it later.
+ */
+ args = simplify_copy_function_arguments(*old_args, result_type, func_tuple);
+ else
+ /* Copy argument list before we start mutating it */
+ args = list_copy(*old_args);
+
+ /* Reduce constants in the FuncExpr's arguments */
+ foreach(lc, args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+ bool isCachable = true;
+
+ arg = const_expressions_mutator(arg, context, &isCachable);
+ lfirst(lc) = arg;
+
+ /*
+ * We're stuck in a catch-22 here. If all arguments and the call
+ * itself is cachable, we don't want to insert cache nodes for
+ * arguments. But we don't know that until we walk through all the
+ * arguments.
+ *
+ * So we accumulate cachable arguments in a list of ListCell pointers,
+ * which we will update later if necessary.
+ *
+ * Note: The args list may not be mutated from here on this until we
+ * handle cachable_args below.
+ */
+ if (isCachable)
+ {
+ if (is_cache_useful((Expr *) arg))
+ cachable_args = lappend(cachable_args, &lfirst(lc));
+ }
+ else
+ *cachable = false; /* One bad arg spoils the whole cache */
+ }
+
/*
! * evaluate_function tells us about the cachability of the function call
*/
newexpr = evaluate_function(funcid, result_type, result_typmod,
! result_collid, input_collid, args,
! func_tuple, context, cachable);
/*
* Some functions calls can be simplified at plan time based on properties
*************** simplify_function(Expr *oldexpr, Oid fun
*** 3673,3702 ****
PointerGetDatum(oldexpr)));
if (!newexpr && allow_inline)
newexpr = inline_function(funcid, result_type, result_collid,
! input_collid, *args,
! func_tuple, context);
ReleaseSysCache(func_tuple);
return newexpr;
}
/*
* reorder_function_arguments: convert named-notation args to positional args
*
* This function also inserts default argument values as needed, since it's
* impossible to form a truly valid positional call without that.
*/
static List *
! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple,
! eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int pronargs = funcform->pronargs;
int nargsprovided = list_length(args);
Node *argarray[FUNC_MAX_ARGS];
- Bitmapset *defargnumbers;
ListCell *lc;
int i;
--- 3984,4094 ----
PointerGetDatum(oldexpr)));
if (!newexpr && allow_inline)
+ {
+ /*
+ * The inlined expression may be cachable regardless of the above, if
+ * the function's volatility was mis-labeled or if volatile parts are
+ * removed (possible due to constant folding of conditionals).
+ *
+ * inline_function() also takes care of caching all cachable subtrees
+ */
+ bool isCachable = true;
+
newexpr = inline_function(funcid, result_type, result_collid,
! input_collid, args,
! func_tuple, context, &isCachable);
!
! if (newexpr)
! *cachable = isCachable;
! }
ReleaseSysCache(func_tuple);
+ /*
+ * If function call can't be cached/inlined, update all cachable arguments
+ */
+ if (!newexpr && !(*cachable))
+ {
+ foreach(lc, cachable_args)
+ {
+ Node **arg = (Node **) lfirst(lc);
+
+ *arg = (Node *) makeCacheExpr((Expr *) *arg);
+ }
+ }
+
+ /* Argument processing done, give it back to the caller */
+ *old_args = args;
+
return newexpr;
}
/*
+ * This function prepares a function's argument list -- converting
+ * named-notation argument list into positional notation while adding any
+ * needed default argument expressions.
+ *
+ * Always returns a copy of the argument list, the original list is not
+ * modified.
+ */
+ static List *
+ simplify_copy_function_arguments(List *old_args, Oid result_type,
+ HeapTuple func_tuple)
+ {
+ List *args = NIL;
+ ListCell *lc;
+ bool has_named_args = false;
+ int nargs_before;
+
+ /* Do we need to reorder named arguments? */
+ foreach(lc, old_args)
+ {
+ Node *arg = (Node *) lfirst(lc);
+
+ if (IsA(arg, NamedArgExpr))
+ {
+ has_named_args = true;
+ break;
+ }
+ }
+
+ nargs_before = list_length(old_args);
+
+ /*
+ * Reorder named arguments and add default arguments if needed.
+ */
+ if (has_named_args)
+ args = reorder_function_arguments(old_args, result_type, func_tuple);
+
+ else
+ {
+ args = list_copy(old_args);
+
+ /* Append missing default arguments to the list */
+ if (((Form_pg_proc) GETSTRUCT(func_tuple))->pronargs > list_length(args))
+ args = add_function_defaults(args, result_type, func_tuple);
+ }
+
+ if (list_length(args) != nargs_before)
+ /* Added defaults may need casts */
+ recheck_cast_function_args(args, result_type, func_tuple);
+
+ return args;
+ }
+
+ /*
* reorder_function_arguments: convert named-notation args to positional args
*
* This function also inserts default argument values as needed, since it's
* impossible to form a truly valid positional call without that.
*/
static List *
! reorder_function_arguments(List *args, Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
int pronargs = funcform->pronargs;
int nargsprovided = list_length(args);
Node *argarray[FUNC_MAX_ARGS];
ListCell *lc;
int i;
*************** reorder_function_arguments(List *args, O
*** 3730,3736 ****
* Fetch default expressions, if needed, and insert into array at proper
* locations (they aren't necessarily consecutive or all used)
*/
- defargnumbers = NULL;
if (nargsprovided < pronargs)
{
List *defaults = fetch_function_defaults(func_tuple);
--- 4122,4127 ----
*************** reorder_function_arguments(List *args, O
*** 3739,3748 ****
foreach(lc, defaults)
{
if (argarray[i] == NULL)
- {
argarray[i] = (Node *) lfirst(lc);
- defargnumbers = bms_add_member(defargnumbers, i);
- }
i++;
}
}
--- 4130,4136 ----
*************** reorder_function_arguments(List *args, O
*** 3755,3786 ****
args = lappend(args, argarray[i]);
}
- /* Recheck argument types and add casts if needed */
- recheck_cast_function_args(args, result_type, func_tuple);
-
- /*
- * Lastly, we have to recursively simplify the defaults we just added (but
- * don't recurse on the args passed in, as we already did those). This
- * isn't merely an optimization, it's *necessary* since there could be
- * functions with named or defaulted arguments down in there.
- *
- * Note that we do this last in hopes of simplifying any typecasts that
- * were added by recheck_cast_function_args --- there shouldn't be any new
- * casts added to the explicit arguments, but casts on the defaults are
- * possible.
- */
- if (defargnumbers != NULL)
- {
- i = 0;
- foreach(lc, args)
- {
- if (bms_is_member(i, defargnumbers))
- lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
- context);
- i++;
- }
- }
-
return args;
}
--- 4143,4148 ----
*************** reorder_function_arguments(List *args, O
*** 3791,3810 ****
* and so we know we just need to add defaults at the end.
*/
static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple,
! eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
- int nargsprovided = list_length(args);
List *defaults;
int ndelete;
- ListCell *lc;
/* Get all the default expressions from the pg_proc tuple */
defaults = fetch_function_defaults(func_tuple);
/* Delete any unused defaults from the list */
! ndelete = nargsprovided + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
--- 4153,4169 ----
* and so we know we just need to add defaults at the end.
*/
static List *
! add_function_defaults(List *args, Oid result_type, HeapTuple func_tuple)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
List *defaults;
int ndelete;
/* Get all the default expressions from the pg_proc tuple */
defaults = fetch_function_defaults(func_tuple);
/* Delete any unused defaults from the list */
! ndelete = list_length(args) + list_length(defaults) - funcform->pronargs;
if (ndelete < 0)
elog(ERROR, "not enough default arguments");
while (ndelete-- > 0)
*************** add_function_defaults(List *args, Oid re
*** 3813,3840 ****
/* And form the combined argument list */
args = list_concat(args, defaults);
- /* Recheck argument types and add casts if needed */
- recheck_cast_function_args(args, result_type, func_tuple);
-
- /*
- * Lastly, we have to recursively simplify the defaults we just added (but
- * don't recurse on the args passed in, as we already did those). This
- * isn't merely an optimization, it's *necessary* since there could be
- * functions with named or defaulted arguments down in there.
- *
- * Note that we do this last in hopes of simplifying any typecasts that
- * were added by recheck_cast_function_args --- there shouldn't be any new
- * casts added to the explicit arguments, but casts on the defaults are
- * possible.
- */
- foreach(lc, args)
- {
- if (nargsprovided-- > 0)
- continue; /* skip original arg positions */
- lfirst(lc) = eval_const_expressions_mutator((Node *) lfirst(lc),
- context);
- }
-
return args;
}
--- 4172,4177 ----
*************** static Expr *
*** 3924,3930 ****
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
bool has_nonconst_input = false;
--- 4261,4268 ----
evaluate_function(Oid funcid, Oid result_type, int32 result_typmod,
Oid result_collid, Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context,
! bool *cachable)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
bool has_nonconst_input = false;
*************** evaluate_function(Oid funcid, Oid result
*** 3936,3942 ****
--- 4274,4283 ----
* Can't simplify if it returns a set.
*/
if (funcform->proretset)
+ {
+ *cachable = false;
return NULL;
+ }
/*
* Can't simplify if it returns RECORD. The immediate problem is that it
*************** evaluate_function(Oid funcid, Oid result
*** 3950,3956 ****
--- 4291,4303 ----
* gotchas, seems best to leave the function call unreduced.
*/
if (funcform->prorettype == RECORDOID)
+ {
+ *cachable = false;
return NULL;
+ }
+
+ if (funcform->provolatile == PROVOLATILE_VOLATILE)
+ *cachable = false;
/*
* Check for constant inputs and especially constant-NULL inputs.
*************** static Expr *
*** 4047,4053 ****
inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
char *src;
--- 4394,4401 ----
inline_function(Oid funcid, Oid result_type, Oid result_collid,
Oid input_collid, List *args,
HeapTuple func_tuple,
! eval_const_expressions_context *context,
! bool *cachable)
{
Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple);
char *src;
*************** inline_function(Oid funcid, Oid result_t
*** 4326,4332 ****
* the current function to the context list of active functions.
*/
context->active_fns = lcons_oid(funcid, context->active_fns);
! newexpr = eval_const_expressions_mutator(newexpr, context);
context->active_fns = list_delete_first(context->active_fns);
error_context_stack = sqlerrcontext.previous;
--- 4674,4680 ----
* the current function to the context list of active functions.
*/
context->active_fns = lcons_oid(funcid, context->active_fns);
! newexpr = const_expressions_mutator(newexpr, context, cachable);
context->active_fns = list_delete_first(context->active_fns);
error_context_stack = sqlerrcontext.previous;
*************** sql_inline_error_callback(void *arg)
*** 4406,4411 ****
--- 4754,4787 ----
}
/*
+ * Is it useful to cache this expression? Constants and param references are
+ * always fast to access so don't insert cache in front of those.
+ *
+ * Without inline, we lose almost 10% time in some very simple queries (!)
+ */
+ static inline bool
+ is_cache_useful(Expr *expr)
+ {
+ if (IsA(expr, Const))
+ return false;
+ if (IsA(expr, Param))
+ return false;
+ return true;
+ }
+
+ static Expr *
+ insert_cache(Expr *expr)
+ {
+ /* Don't cache obviously cheap expressions */
+ if (!is_cache_useful(expr))
+ return expr;
+
+ Assert(!IsA(expr, CacheExpr));
+
+ return (Expr *) makeCacheExpr(expr);
+ }
+
+ /*
* evaluate_expr: pre-evaluate a constant expression
*
* We use the executor's routine ExecEvalExpr() to avoid duplication of
*************** evaluate_expr(Expr *expr, Oid result_typ
*** 4438,4444 ****
* Prepare expr for execution. (Note: we can't use ExecPrepareExpr
* because it'd result in recursively invoking eval_const_expressions.)
*/
! exprstate = ExecInitExpr(expr, NULL);
/*
* And evaluate it.
--- 4814,4820 ----
* Prepare expr for execution. (Note: we can't use ExecPrepareExpr
* because it'd result in recursively invoking eval_const_expressions.)
*/
! exprstate = ExecInitExpr(expr, NULL, false);
/*
* And evaluate it.
diff --git a/src/backend/optimizer/util/predtest.c b/src/backend/optimizer/util/predtest.c
new file mode 100644
index c3161c5..f98854d
*** a/src/backend/optimizer/util/predtest.c
--- b/src/backend/optimizer/util/predtest.c
*************** btree_predicate_proof(Expr *predicate, N
*** 1486,1492 ****
fix_opfuncids((Node *) test_expr);
/* Prepare it for execution */
! test_exprstate = ExecInitExpr(test_expr, NULL);
/* And execute it. */
test_result = ExecEvalExprSwitchContext(test_exprstate,
--- 1486,1492 ----
fix_opfuncids((Node *) test_expr);
/* Prepare it for execution */
! test_exprstate = ExecInitExpr(test_expr, NULL, false);
/* And execute it. */
test_result = ExecEvalExprSwitchContext(test_exprstate,
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
new file mode 100644
index 64ba8ec..e97d39f
*** a/src/backend/utils/adt/ruleutils.c
--- b/src/backend/utils/adt/ruleutils.c
*************** isSimpleNode(Node *node, Node *parentNod
*** 4704,4711 ****
/* function-like: name(..) or name[..] */
return true;
! /* CASE keywords act as parentheses */
case T_CaseExpr:
return true;
case T_FieldSelect:
--- 4704,4715 ----
/* function-like: name(..) or name[..] */
return true;
! case T_CacheExpr:
! /* hidden from user */
! return true;
!
case T_CaseExpr:
+ /* CASE keywords act as parentheses */
return true;
case T_FieldSelect:
*************** get_rule_expr(Node *node, deparse_contex
*** 5826,5831 ****
--- 5830,5849 ----
}
break;
+ case T_CacheExpr:
+ {
+ CacheExpr *cache = (CacheExpr *) node;
+
+ #ifdef DEBUG_CACHEEXPR
+ appendStringInfo(buf, "CACHE[");
+ get_rule_expr((Node *) cache->arg, context, true);
+ appendStringInfoChar(buf, ']');
+ #else
+ get_rule_expr((Node *) cache->arg, context, true);
+ #endif
+ }
+ break;
+
case T_CoerceToDomain:
{
CoerceToDomain *ctest = (CoerceToDomain *) node;
diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h
new file mode 100644
index 7f27669..fa08344
*** a/src/include/executor/executor.h
--- b/src/include/executor/executor.h
*************** extern Tuplestorestate *ExecMakeTableFun
*** 227,233 ****
bool randomAccess);
extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
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 int ExecTargetListLength(List *targetlist);
--- 227,233 ----
bool randomAccess);
extern Datum ExecEvalExprSwitchContext(ExprState *expression, ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
! extern ExprState *ExecInitExpr(Expr *node, PlanState *parent, bool cacheEnabled);
extern ExprState *ExecPrepareExpr(Expr *node, EState *estate);
extern bool ExecQual(List *qual, ExprContext *econtext, bool resultForNull);
extern int ExecTargetListLength(List *targetlist);
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
new file mode 100644
index 5207102..a4701f2
*** a/src/include/nodes/execnodes.h
--- b/src/include/nodes/execnodes.h
*************** typedef struct FuncExprState
*** 674,679 ****
--- 674,696 ----
} FuncExprState;
/* ----------------
+ * CacheExprState node
+ *
+ * Takes care of caching execution-time constants that cannot be constant
+ * folded at plan-time.
+ * ----------------
+ */
+ typedef struct CacheExprState
+ {
+ ExprState xprstate;
+ ExprState *arg; /* state of sub-expression */
+
+ bool enabled; /* is cache enabled? */
+ Datum result; /* cached result */
+ bool isNull; /* is result NULL? */
+ } CacheExprState;
+
+ /* ----------------
* ScalarArrayOpExprState node
*
* This is a FuncExprState plus some additional data.
diff --git a/src/include/nodes/makefuncs.h b/src/include/nodes/makefuncs.h
new file mode 100644
index a6dffe6..fa5d8e7
*** a/src/include/nodes/makefuncs.h
--- b/src/include/nodes/makefuncs.h
*************** extern TypeName *makeTypeNameFromOid(Oid
*** 74,79 ****
--- 74,80 ----
extern FuncExpr *makeFuncExpr(Oid funcid, Oid rettype, List *args,
Oid funccollid, Oid inputcollid, CoercionForm fformat);
+ extern CacheExpr *makeCacheExpr(Expr *arg);
extern DefElem *makeDefElem(char *name, Node *arg);
extern DefElem *makeDefElemExtended(char *nameSpace, char *name, Node *arg,
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
new file mode 100644
index 0e7d184..016a63c
*** a/src/include/nodes/nodes.h
--- b/src/include/nodes/nodes.h
*************** typedef enum NodeTag
*** 172,177 ****
--- 172,178 ----
T_JoinExpr,
T_FromExpr,
T_IntoClause,
+ T_CacheExpr,
/*
* TAGS FOR EXPRESSION STATE NODES (execnodes.h)
*************** typedef enum NodeTag
*** 205,210 ****
--- 206,212 ----
T_NullTestState,
T_CoerceToDomainState,
T_DomainConstraintState,
+ T_CacheExprState,
/*
* TAGS FOR PLANNER NODES (relation.h)
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
new file mode 100644
index 50831ee..95908c5
*** a/src/include/nodes/primnodes.h
--- b/src/include/nodes/primnodes.h
*************** typedef struct BooleanTest
*** 1033,1038 ****
--- 1033,1053 ----
} BooleanTest;
/*
+ * CacheExpr
+ *
+ * CacheExpr is a constant expression to be cached at execution time. The
+ * eval_const_expressions() function inserts CacheExpr nodes nodes at
+ * strategic locations when it recognizes constant expressions that cannot be
+ * constant-folded at plan time, such as expressions with Param references,
+ * stable function and operator calls with constant arguments, etc
+ */
+ typedef struct CacheExpr
+ {
+ Expr xpr;
+ Expr *arg;
+ } CacheExpr;
+
+ /*
* CoerceToDomain
*
* CoerceToDomain represents the operation of coercing a value to a domain
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
new file mode 100644
index 0b126a3..58e4c15
*** a/src/pl/plpgsql/src/pl_exec.c
--- b/src/pl/plpgsql/src/pl_exec.c
*************** exec_eval_simple_expr(PLpgSQL_execstate
*** 4938,4944 ****
if (expr->expr_simple_lxid != curlxid)
{
oldcontext = MemoryContextSwitchTo(simple_eval_estate->es_query_cxt);
! expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr, NULL);
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = curlxid;
MemoryContextSwitchTo(oldcontext);
--- 4938,4946 ----
if (expr->expr_simple_lxid != curlxid)
{
oldcontext = MemoryContextSwitchTo(simple_eval_estate->es_query_cxt);
! expr->expr_simple_state = ExecInitExpr(expr->expr_simple_expr,
! NULL,
! false);
expr->expr_simple_in_use = false;
expr->expr_simple_lxid = curlxid;
MemoryContextSwitchTo(oldcontext);
*************** exec_simple_check_node(Node *node)
*** 5472,5477 ****
--- 5474,5484 ----
return TRUE;
case T_Param:
+ /*
+ * If we have other kinds of params here, then earlier tests
+ * should have ruled out this as simple expression
+ */
+ Assert(((Param *) node)->paramkind == PARAM_EXTERN);
return TRUE;
case T_ArrayRef:
*************** exec_simple_check_node(Node *node)
*** 5704,5709 ****
--- 5711,5720 ----
return TRUE;
}
+ case T_CacheExpr:
+ /* Caching is disabled for simple expressions */
+ return TRUE;
+
default:
return FALSE;
}
diff --git a/src/test/regress/expected/cache.out b/src/test/regress/expected/cache.out
new file mode 100644
index ...592263d
*** a/src/test/regress/expected/cache.out
--- b/src/test/regress/expected/cache.out
***************
*** 0 ****
--- 1,685 ----
+ --
+ -- Test cachable expressions
+ --
+ -- If the NOTICE outputs of these functions change, you've probably broken
+ -- something with the CacheExpr optimization
+ --
+ create function stable_true() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE TRUE'; return true; end$$;
+ create function volatile_true() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE TRUE'; return true; end$$;
+ create function stable_false() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE FALSE'; return false; end$$;
+ create function volatile_false() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE FALSE'; return false; end$$;
+ -- Table with two rows
+ create table two (i int);
+ insert into two values (1), (2);
+ -- Boolean expressions
+ select stable_false() or volatile_true() or stable_true() as b from two;
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select stable_true() or volatile_false() or stable_false() as b from two;
+ NOTICE: STABLE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select stable_false() or volatile_true() as b from two;
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select stable_false() or stable_false() or volatile_true() as b from two;
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select volatile_true() or volatile_false() or stable_false() as b from two;
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select volatile_false() or volatile_true() or stable_false() as b from two;
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select stable_true() and volatile_false() and stable_false() as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select stable_false() and volatile_true() and stable_true() as b from two;
+ NOTICE: STABLE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select stable_true() and volatile_false() as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select stable_true() and stable_true() and volatile_false() as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select volatile_true() and volatile_false() and stable_true() as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select volatile_false() and volatile_true() and stable_true() as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select not stable_true() as b from two;
+ NOTICE: STABLE TRUE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select not volatile_true() as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ -- Bind params
+ prepare param_test(bool) as select $1 or stable_false() or volatile_true() as b from two;
+ execute param_test(true);
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ execute param_test(false);
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- Function calls
+ create function stable(bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%)', $1; return $1; end$$;
+ create function volatile(bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%)', $1; return $1; end$$;
+ select volatile(volatile_true()) from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE(t)
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE(t)
+ volatile
+ ----------
+ t
+ t
+ (2 rows)
+
+ select stable(stable_true()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE(t)
+ stable
+ --------
+ t
+ t
+ (2 rows)
+
+ select stable(volatile_true()) from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(t)
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(t)
+ stable
+ --------
+ t
+ t
+ (2 rows)
+
+ select volatile(stable_true()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE(t)
+ NOTICE: VOLATILE(t)
+ volatile
+ ----------
+ t
+ t
+ (2 rows)
+
+ create function stable(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ create function volatile(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%, %)', $1, $2; return $1; end$$;
+ select stable(volatile_true(), volatile_false()) from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE(t, f)
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE(t, f)
+ stable
+ --------
+ t
+ t
+ (2 rows)
+
+ select stable(stable_true(), volatile_false()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE(t, f)
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE(t, f)
+ stable
+ --------
+ t
+ t
+ (2 rows)
+
+ select stable(stable_true(), stable_false()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE(t, f)
+ stable
+ --------
+ t
+ t
+ (2 rows)
+
+ select volatile(volatile_true(), volatile_false()) from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE(t, f)
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE(t, f)
+ volatile
+ ----------
+ t
+ t
+ (2 rows)
+
+ select volatile(stable_true(), volatile_false()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE(t, f)
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE(t, f)
+ volatile
+ ----------
+ t
+ t
+ (2 rows)
+
+ select volatile(stable_true(), stable_false()) from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE(t, f)
+ NOTICE: VOLATILE(t, f)
+ volatile
+ ----------
+ t
+ t
+ (2 rows)
+
+ -- Default arguments
+ create function stable_def(a bool = stable_false(), b bool = volatile_true())
+ returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ select stable_def() from two;
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(f, t)
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(f, t)
+ stable_def
+ ------------
+ f
+ f
+ (2 rows)
+
+ select stable_def(b := stable_true()) from two;
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE(f, t)
+ stable_def
+ ------------
+ f
+ f
+ (2 rows)
+
+ select stable_def(volatile_false()) from two;
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(f, t)
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE(f, t)
+ stable_def
+ ------------
+ f
+ f
+ (2 rows)
+
+ -- Operators
+ create function stable_eq(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE % == %', $1, $2; return $1 = $2; end$$;
+ create function volatile_eq(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE % =%%= %', $1, $2; return $1 = $2; end$$;
+ create operator == (procedure = stable_eq, leftarg=bool, rightarg=bool);
+ create operator =%= (procedure = volatile_eq, leftarg=bool, rightarg=bool);
+ select volatile_true() == volatile_false() from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE t == f
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE t == f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select stable_true() == volatile_false() from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE t == f
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE t == f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select stable_true() == stable_false() from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE t == f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select volatile_true() =%= volatile_false() from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE t =%= f
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE t =%= f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select stable_true() =%= volatile_false() from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE t =%= f
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE t =%= f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select stable_true() =%= stable_false() from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ NOTICE: VOLATILE t =%= f
+ NOTICE: VOLATILE t =%= f
+ ?column?
+ ----------
+ f
+ f
+ (2 rows)
+
+ select (volatile_true() or stable_true()) == true as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE t == t
+ NOTICE: STABLE t == t
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- Coalesce
+ create function stable_null() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE NULL'; return null; end$$;
+ create function volatile_null() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE NULL'; return null; end$$;
+ select coalesce(stable_null(), stable_true()) from two;
+ NOTICE: STABLE NULL
+ NOTICE: STABLE TRUE
+ coalesce
+ ----------
+ t
+ t
+ (2 rows)
+
+ select coalesce(stable_true(), volatile_null()) from two;
+ NOTICE: STABLE TRUE
+ coalesce
+ ----------
+ t
+ t
+ (2 rows)
+
+ select coalesce(volatile_null(), stable_null(), volatile_true()) from two;
+ NOTICE: VOLATILE NULL
+ NOTICE: STABLE NULL
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE NULL
+ NOTICE: VOLATILE TRUE
+ coalesce
+ ----------
+ t
+ t
+ (2 rows)
+
+ -- Case/when
+ select case when stable_true() then 't' else volatile_false() end as b from two;
+ NOTICE: STABLE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select case when volatile_true() then stable_true() else stable_false() end as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select case when i=1 then stable_true() else stable_false() end as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ b
+ ---
+ t
+ f
+ (2 rows)
+
+ select case when i=1 then volatile_true() else volatile_false() end as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ t
+ f
+ (2 rows)
+
+ select case when 't' then 't' else volatile_false() end == true as b from two;
+ NOTICE: STABLE t == t
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- Coerce via I/O
+ select stable_true()::text::bool == true as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE t == t
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select volatile_true()::text::bool == true as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE t == t
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE t == t
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- IS DISTINCT FROM
+ select (stable_true() is not distinct from volatile_false()) as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select (stable_true() is distinct from stable_false()) == false as b from two;
+ NOTICE: STABLE TRUE
+ NOTICE: STABLE FALSE
+ NOTICE: STABLE t == f
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select (volatile_true() is distinct from null) as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- IS NULL
+ select volatile_true() is null == false as b from two;
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE f == f
+ NOTICE: VOLATILE TRUE
+ NOTICE: STABLE f == f
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ select stable_null() is not null == true as b from two;
+ NOTICE: STABLE NULL
+ NOTICE: STABLE f == t
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ -- Boolean tests
+ select volatile_false() is true == true as b from two;
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE f == t
+ NOTICE: VOLATILE FALSE
+ NOTICE: STABLE f == t
+ b
+ ---
+ f
+ f
+ (2 rows)
+
+ select stable_null() is not unknown == false as b from two;
+ NOTICE: STABLE NULL
+ NOTICE: STABLE f == f
+ b
+ ---
+ t
+ t
+ (2 rows)
+
+ -- Field select -- not currently cached
+ create function stable_row(a out int, b out int) STABLE language plpgsql as
+ $$begin raise notice 'STABLE ROW'; a = 1; b = 2; end$$;
+ select (stable_row()).a from two;
+ NOTICE: STABLE ROW
+ NOTICE: STABLE ROW
+ a
+ ---
+ 1
+ 1
+ (2 rows)
+
+ -- WHERE clause
+ begin;
+ -- stable_true is evaluated twice due to planning estimates
+ declare stable_where cursor for select * from two where i > stable_true()::int;
+ NOTICE: STABLE TRUE
+ fetch all from stable_where;
+ NOTICE: STABLE TRUE
+ i
+ ---
+ 2
+ (1 row)
+
+ declare volatile_where cursor for select * from two where i = volatile_false()::int;
+ fetch all from volatile_where;
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ i
+ ---
+ (0 rows)
+
+ rollback;
+ -- INSERT column default expressions
+ create table defaults (
+ dummy int,
+ a bool default stable_true(),
+ b bool default volatile_true()
+ );
+ insert into defaults (dummy) values(0), (1);
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ -- ALTER COLUMN TYPE USING
+ alter table defaults alter column a type bool using stable_false();
+ NOTICE: STABLE FALSE
+ alter table defaults alter column a type bool using volatile_false();
+ NOTICE: VOLATILE FALSE
+ NOTICE: VOLATILE FALSE
+ -- COPY FROM with default expressions
+ copy defaults (dummy) from stdin;
+ NOTICE: STABLE TRUE
+ CONTEXT: COPY defaults, line 1: "2"
+ NOTICE: VOLATILE TRUE
+ CONTEXT: COPY defaults, line 1: "2"
+ NOTICE: VOLATILE TRUE
+ CONTEXT: COPY defaults, line 2: "3"
+ -- VALUES list expressions
+ -- The fact that there are be 3 lines of 'VOLATILE TRUE' output is a quirk of
+ -- the current set-returning function execution code
+ insert into defaults (dummy, a, b)
+ values (generate_series(4, 5), stable_true(), volatile_true());
+ NOTICE: STABLE TRUE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ NOTICE: VOLATILE TRUE
+ -- PL/pgSQL Simple expressions
+ -- Make sure we don't cache simple expressions -- these expressions are only
+ -- initialized once per transaction and then executed multiple times
+ create function stable_max() returns int STABLE language plpgsql as
+ $$begin return (select max(i) from two); end$$;
+ create function simple() returns int STABLE language plpgsql as
+ $$begin return stable_max(); end$$;
+ begin;
+ select simple();
+ simple
+ --------
+ 2
+ (1 row)
+
+ insert into two values(3);
+ select simple();
+ simple
+ --------
+ 3
+ (1 row)
+
+ rollback;
+ -- The end
+ drop table defaults;
+ drop table two;
diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule
new file mode 100644
index 8852e0a..2c15203
*** a/src/test/regress/parallel_schedule
--- b/src/test/regress/parallel_schedule
*************** test: select_into select_distinct select
*** 83,89 ****
# ----------
# Another group of parallel tests
# ----------
! test: privileges security_label collate
test: misc
# rules cannot run concurrently with any test that creates a view
--- 83,89 ----
# ----------
# Another group of parallel tests
# ----------
! test: privileges security_label collate cache
test: misc
# rules cannot run concurrently with any test that creates a view
diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule
new file mode 100644
index 0bc5df7..972a722
*** a/src/test/regress/serial_schedule
--- b/src/test/regress/serial_schedule
*************** test: prepared_xacts
*** 94,99 ****
--- 94,100 ----
test: privileges
test: security_label
test: collate
+ test: cache
test: misc
test: rules
test: select_views
diff --git a/src/test/regress/sql/cache.sql b/src/test/regress/sql/cache.sql
new file mode 100644
index ...9b68670
*** a/src/test/regress/sql/cache.sql
--- b/src/test/regress/sql/cache.sql
***************
*** 0 ****
--- 1,185 ----
+ --
+ -- Test cachable expressions
+ --
+ -- If the NOTICE outputs of these functions change, you've probably broken
+ -- something with the CacheExpr optimization
+ --
+
+ create function stable_true() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE TRUE'; return true; end$$;
+ create function volatile_true() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE TRUE'; return true; end$$;
+ create function stable_false() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE FALSE'; return false; end$$;
+ create function volatile_false() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE FALSE'; return false; end$$;
+
+ -- Table with two rows
+ create table two (i int);
+ insert into two values (1), (2);
+
+ -- Boolean expressions
+ select stable_false() or volatile_true() or stable_true() as b from two;
+ select stable_true() or volatile_false() or stable_false() as b from two;
+ select stable_false() or volatile_true() as b from two;
+ select stable_false() or stable_false() or volatile_true() as b from two;
+ select volatile_true() or volatile_false() or stable_false() as b from two;
+ select volatile_false() or volatile_true() or stable_false() as b from two;
+
+ select stable_true() and volatile_false() and stable_false() as b from two;
+ select stable_false() and volatile_true() and stable_true() as b from two;
+ select stable_true() and volatile_false() as b from two;
+ select stable_true() and stable_true() and volatile_false() as b from two;
+ select volatile_true() and volatile_false() and stable_true() as b from two;
+ select volatile_false() and volatile_true() and stable_true() as b from two;
+
+ select not stable_true() as b from two;
+ select not volatile_true() as b from two;
+
+ -- Bind params
+ prepare param_test(bool) as select $1 or stable_false() or volatile_true() as b from two;
+ execute param_test(true);
+ execute param_test(false);
+
+ -- Function calls
+ create function stable(bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%)', $1; return $1; end$$;
+ create function volatile(bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%)', $1; return $1; end$$;
+
+ select volatile(volatile_true()) from two;
+ select stable(stable_true()) from two;
+ select stable(volatile_true()) from two;
+ select volatile(stable_true()) from two;
+
+ create function stable(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+ create function volatile(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE(%, %)', $1, $2; return $1; end$$;
+
+ select stable(volatile_true(), volatile_false()) from two;
+ select stable(stable_true(), volatile_false()) from two;
+ select stable(stable_true(), stable_false()) from two;
+ select volatile(volatile_true(), volatile_false()) from two;
+ select volatile(stable_true(), volatile_false()) from two;
+ select volatile(stable_true(), stable_false()) from two;
+
+ -- Default arguments
+ create function stable_def(a bool = stable_false(), b bool = volatile_true())
+ returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE(%, %)', $1, $2; return $1; end$$;
+
+ select stable_def() from two;
+ select stable_def(b := stable_true()) from two;
+ select stable_def(volatile_false()) from two;
+
+ -- Operators
+ create function stable_eq(bool, bool) returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE % == %', $1, $2; return $1 = $2; end$$;
+ create function volatile_eq(bool, bool) returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE % =%%= %', $1, $2; return $1 = $2; end$$;
+
+ create operator == (procedure = stable_eq, leftarg=bool, rightarg=bool);
+ create operator =%= (procedure = volatile_eq, leftarg=bool, rightarg=bool);
+
+ select volatile_true() == volatile_false() from two;
+ select stable_true() == volatile_false() from two;
+ select stable_true() == stable_false() from two;
+ select volatile_true() =%= volatile_false() from two;
+ select stable_true() =%= volatile_false() from two;
+ select stable_true() =%= stable_false() from two;
+
+ select (volatile_true() or stable_true()) == true as b from two;
+
+ -- Coalesce
+ create function stable_null() returns bool STABLE language plpgsql as
+ $$begin raise notice 'STABLE NULL'; return null; end$$;
+ create function volatile_null() returns bool VOLATILE language plpgsql as
+ $$begin raise notice 'VOLATILE NULL'; return null; end$$;
+
+ select coalesce(stable_null(), stable_true()) from two;
+ select coalesce(stable_true(), volatile_null()) from two;
+ select coalesce(volatile_null(), stable_null(), volatile_true()) from two;
+
+ -- Case/when
+ select case when stable_true() then 't' else volatile_false() end as b from two;
+ select case when volatile_true() then stable_true() else stable_false() end as b from two;
+ select case when i=1 then stable_true() else stable_false() end as b from two;
+ select case when i=1 then volatile_true() else volatile_false() end as b from two;
+
+ select case when 't' then 't' else volatile_false() end == true as b from two;
+
+ -- Coerce via I/O
+ select stable_true()::text::bool == true as b from two;
+ select volatile_true()::text::bool == true as b from two;
+
+ -- IS DISTINCT FROM
+ select (stable_true() is not distinct from volatile_false()) as b from two;
+ select (stable_true() is distinct from stable_false()) == false as b from two;
+ select (volatile_true() is distinct from null) as b from two;
+
+ -- IS NULL
+ select volatile_true() is null == false as b from two;
+ select stable_null() is not null == true as b from two;
+
+ -- Boolean tests
+ select volatile_false() is true == true as b from two;
+ select stable_null() is not unknown == false as b from two;
+
+ -- Field select -- not currently cached
+ create function stable_row(a out int, b out int) STABLE language plpgsql as
+ $$begin raise notice 'STABLE ROW'; a = 1; b = 2; end$$;
+
+ select (stable_row()).a from two;
+
+ -- WHERE clause
+ begin;
+ -- stable_true is evaluated twice due to planning estimates
+ declare stable_where cursor for select * from two where i > stable_true()::int;
+ fetch all from stable_where;
+ declare volatile_where cursor for select * from two where i = volatile_false()::int;
+ fetch all from volatile_where;
+ rollback;
+
+ -- INSERT column default expressions
+ create table defaults (
+ dummy int,
+ a bool default stable_true(),
+ b bool default volatile_true()
+ );
+ insert into defaults (dummy) values(0), (1);
+
+ -- ALTER COLUMN TYPE USING
+ alter table defaults alter column a type bool using stable_false();
+ alter table defaults alter column a type bool using volatile_false();
+
+ -- COPY FROM with default expressions
+ copy defaults (dummy) from stdin;
+ 2
+ 3
+ \.
+
+ -- VALUES list expressions
+ -- The fact that there are be 3 lines of 'VOLATILE TRUE' output is a quirk of
+ -- the current set-returning function execution code
+ insert into defaults (dummy, a, b)
+ values (generate_series(4, 5), stable_true(), volatile_true());
+
+ -- PL/pgSQL Simple expressions
+ -- Make sure we don't cache simple expressions -- these expressions are only
+ -- initialized once per transaction and then executed multiple times
+ create function stable_max() returns int STABLE language plpgsql as
+ $$begin return (select max(i) from two); end$$;
+
+ create function simple() returns int STABLE language plpgsql as
+ $$begin return stable_max(); end$$;
+
+ begin;
+ select simple();
+ insert into two values(3);
+ select simple();
+ rollback;
+
+ -- The end
+ drop table defaults;
+ drop table two;